# call bind apply
## this
this永远指向最后调用他的对象
例如:
~~~
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
~~~
这里最后调用fn的是window,所以指向window的name属性。
再看一个
~~~
var name = "windowsName";
var a = {
name : null,
// name: "Cherry",
fn : function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
~~~
这里虽然f是a.fn 但是f并没有被直接调用,最后调用f的依然是window,所以输出windowsName。
**箭头函数的this**
> 箭头函数的 this 始终指向函数定义时的 this,而非执行时。,箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。
>
## call bind apply的区别:
call apply区别在于,apply传参传入数组,call传参依次传入。
~~~
func.call(context, arg1, arg2, ...)
func.apply(context, [arg1, arg2, ...])
~~~
> bind与这两者的区别很大,bind函数返回的是一个新的函数,而后两者则都是立即执行函数,使用的时候即调用了该函数,返回方法操作的结果。
~~~
var name = 'Bob';
var person = {
name: 'Alice',
age: 29
}
function sayName() {
console.log(this.name);
}
var say = sayName.bind(person);
say(); // Alice
sayName(); // Bob
~~~
同时需要注意,bind绑定的this,是不可以使用call apply改变的。
## 关于null
在非严格模式下,第一个参数传入null 或 undefined会指向全局变量 也就是window。
## call的实现
~~~
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']')
}
var result = eval('context.fn(' + args + ')');
delete context.fn;
return result;
}
~~~
## apply的实现
~~~
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
~~~
## bind的实现
先来看一下bind怎么使用:
~~~
function test (a,b){
console.log(this.name);
console.log(a);
console.log(b);
}
const mine = {
name:'aaa'
}
const t = test.bind(mine,1,2);
t();
// 或这样
// const t = test.bind(mine,1);
// t(2)
~~~
所以说需要传入this指向,也可以继续传参,作为参数。所以初步考虑的实现是这样的。
~~~
Function.prototype.bind = function (content) {
var self = this;
var outerArgs = Array.prototype.slice.call(arguments, 1);// 这里注意,因为bind在使用中,第一个参数时新指定的this指向,所以在这里,去掉这个参数,从index=1开始,这里全都是函数所需要的参数。
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
var totalArgs = outerArgs.concat(innerArgs);
return self.apply(content, totalArgs);
}
}
~~~
当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。
详情请看:
~~~
var value = 2;
var foo = {
value: 200
}
function bar(name, age) {
this.habit = 'shopping';
console.log('this.value------' + this.value);
console.log('name------' + name);
console.log('age------' + age);
};
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'ddddd');
var obj = new bindFoo('100');
console.log('obj.habit------' + obj.habit);
console.log('obj.friend------' + obj.friend);
// this.value------undefined
// name------ddddd
// age------100
// obj.habit------shopping
// obj.friend------kevin
~~~
所以我们使用这个bind实现方法:
~~~
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
// 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性
// 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
~~~
## 参考
[this call apply bind](https://juejin.im/post/59bfe84351882531b730bac2)
[call apply 实现](https://github.com/mqyqingfeng/Blog/blob/master/articles/%E6%B7%B1%E5%85%A5%E7%B3%BB%E5%88%97%E6%96%87%E7%AB%A0/JavaScript%E6%B7%B1%E5%85%A5%E4%B9%8Bcall%E5%92%8Capply%E7%9A%84%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0.md)
- html
- 冒泡/捕获/委托
- 前端路由
- Dom
- 创建节点API
- 页面修改型API
- 节点查询型API
- 节点关系型API
- 元素属性API
- DOM事件
- classList
- 性能优化
- 节流防抖
- localStorage sessionStorage
- BOM
- meta
- data属性
- js实现拖拽
- html5
- 关于meta
- 轮播图
- js实现拖放
- 电话号inputFormater
- js
- es6
- promise
- iterator
- generator
- async
- proxy
- Set
- Map
- Object的扩展
- String
- Iterator
- Symbol
- 解构赋值
- 函数式编程
- module
- 基本数据类型
- 数组相关codings
- for of/for in
- this
- call bind apply
- 闭包
- 作用域
- prototype与继承
- 深浅拷贝
- setTimeOut/setInterval
- 垃圾处理机制
- 设计模式
- 观察者模式
- 单例模式
- 策略模式
- RegExp
- with
- 其他玩意
- Error/Stack Trace
- 面向对象
- css
- 回流重绘
- %取值
- 属性继承/属性优先级
- flex
- BFC
- 盒模型
- 设置css的方法
- 定位机制
- 块级/行内元素
- hack和一些别的玩意
- css动画
- 几个布局
- 画图形
- css3
- animation对比transform
- 点击不同图片区域跳转不同
- css选择器性能问题
- vh rem em
- css选择器
- 伪类伪元素
- css匹配原理
- 数据结构与算法
- 数据结构
- 树
- 链表
- 栈和队列
- 排序
- 归并排序
- 插入排序
- 选择排序
- 冒泡排序
- 快速排序
- 递归
- 回溯法
- 搜索算法
- 动态规划
- http
- 跨域问题
- CORS
- GET/POST
- ajax
- ajax上传文件
- http缓存
- https
- TCP/UDP
- cookie/session
- http2.0
- spdy
- websocket
- React
- redux
- 生命周期
- 虚拟dom与diff
- 双向数据绑定
- mvvm
- setState
- contextApi props reudx
- 高阶组件
- react-redux
- Fiber
- react-router
- 受控/非受控组件
- 待整理
- webpack
- loader实现
- 前端安全
- 移动端适配
- Vue
- 传值
- 其他