this到底是什么东西
执行上下文的创建阶段,会分别生成变量对象,建立作用域链,确定this指向。
函数的this在调用时绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。为了搞清楚this的指向是什么,必须知道相关函数是如何调用的。
默认绑定
使用不带任何修饰符的的函数引用调用 非严格模式默认指向全局对象
var name = 'window';
var doSth = function(){
console.log(this.name);
}
doSth(); // 'window'
你可能会误以为window.doSth()是调用的,所以是指向window。虽然本例中window.doSth确实等于doSth。name等于window.name。上面代码中这是因为在ES5中,全局变量是挂载在顶层对象(浏览器是window)中
严格模式中 this指向undefined
注意ES6的let
let name2 = 'window2';
let doSth2 = function(){
console.log(this === window);
console.log(this.name2);
}
doSth2() // true, undefined
因为let没有给全局添加属性
隐式绑定
是否由上下文对象调用?绑定到那个上下文对象。
var name = 'window';
var showName = function(){
console.log(this.name);
}
var student = {
name: '咸鱼',
showName: showName,
other: { name: '咸鱼2', showName: showName, }
}
student.showName(); // '咸鱼'
student.other.showName(); // '咸鱼2'
// 用call类比则为:
student.showName.call(student);
// 用call类比则为:
student.other.showName.call(student.other);
隐式丢失
被隐式绑定的函数会丢失绑定对象
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
var a = 'window'
bar(); //window
虽然bar是obj.foo的一个引用,但他引用的是函数本身,因此bar()是不带任何修饰符的的函数调用,使用了默认绑定。
显式绑定
使用###
call、apply、bind方法
call apply bind都是改变函数中的this
- 区别
- 第一个参数都是控制
this指向 - call 第二个参数是arg1,arg2 :参数列表
- apply 第二个参数是数组(或者类数组)
- bind 与call类似不过它的返回值是一个函数
- 第一个参数都是控制
var doSth = function(name){
console.log(this);
console.log(name);
}
doSth.call(2, '咸鱼'); // Number{2}, '咸鱼'
doSth.apply(2, ['咸鱼2']); // Number{2}, '咸鱼2'
doSth.bind(2, '咸鱼3')(); // Number{2}, '咸鱼3'
实现一个 call /bind 函数
- this 参数可以传 null,当为 null 的时候,视为指向 window
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
function called(context) {
const args = Array.prototype.slice.call(arguments, 1);
context.fn = this;
if(context) {
const result = context.fn(...args);
delete context.fn;
return result;
} else {
return this(...args);
}
}
function bound(context) {
const args = Array.prototype.slice.call(arguments, 1);
const fn = this;
return function(...innerArgs) {
const allArgs = [...args, ...innerArgs];
return fn.apply(fn, allArgs);
}
}
new绑定
new到底做了什么?
- 创建了一个对象;
- 该对象的原型,指向了这个 Function(构造函数)的 prototype;
- 该对象实现了这个构造函数的方法;
- 根据一些特定情况返回对象
- 如果没有返回值,则返回创建的对象;
- 如果有返回值,是一个对象,则返回该对象;
- 如果有返回值,不是个对象,则返回创建的对象;
实现一个 new 函数
function newObj(Father) {
if(typeof Father !== "function") {
throw new Error("new operator function the frist param must be a function!")
}
var obj = Object.create(Father.prototype);
var result = Father.apply(obj, Array.prototype.slice.call(arguments, 1));
return result && typeof result === "object" && result !== null ? result : obj;
}
总结
this 的指向,是根据上下文,动态决定的。
- 在简单调用时,this 默认指向的是 window / global / undefined (浏览器/node/严格模式)
- 对象调用时,绑定在对象上;
- 使用 call . apply . bind 时,绑定在指定的参数上;
- 使用 new 关键字是,绑定到新创建的对象上; (以上三条优先级:new > apply/call/bind > 对象调用)
- 使用箭头函数,根据外层的规则决定。