关于this

使用this可以减少传入上下文对象,可以隐式传递一个对象引用。使API简洁而复用,可以自动引用合适的上下文对象。


【要注意的几个点】
1. this不一定指向自身;
2. this不一定指向函数作用域(因为作用域无法通过js代码访问,它存在js引擎内部);
3. this是在运行时(函数被调用时)绑定的,不是编写代码时绑定。this的绑定取决于函数的调用位置,不是函数声明的位置;
4. 不能通过this来引用一个词法作用域内部的东西
【找this的指向】

分析调用栈(为了到达当前执行位置所调用的所有函数),找到当前正在执行的函数的前一个调用,即调用栈的第二个元素。因为函数被调用时,会创建一个活动记录(执行上下文)。该活动记录包括(函数的调用栈、传入的参数、函数的调用方法……)

[方法]:在代码前插入一条debugger语句,在开发者工具中查看call stack中的第二个元素,就是this的调用位置.
function first(){
    //当前调用栈first
    //当前的调用位置是全局作用域
    console.log("first");
    second();//second的调用位置
}
function second(){
    //当前调用栈first->second
    //当前的调用位置是first
    console.log("second");
    third();//third的调用位置
}
function third(){
    //当前调用栈first->second->third
    //当前的调用位置是second
    console.log("third");
}

//first的调用位置
first();//firstsecondthird
【四条this规则】:

1.默认绑定(绑定到全局对象),但严格模式下this会绑定到undefin
ed:
【例子】:不带任何修饰的函数调用

function example(){
    console.log(this.a);
}
var a="karine";
example(); //karine

2.隐式绑定
(在一个对象内部包含一个指向函数的属性,通过该属性间接地引用函数,从而把this间接地绑定到该对象上。)

function example(){
    console.log(this.a);
}
var obj={
    a:"karine",
    example:example
};
obj.example(); //karine

在对象属性引用链中,只有最顶层(最后一层)会影响调用位置。

function example(){
    console.log(this.a);
}
var obj1={
    a:"karine",
    example:example
};
var obj2={
    a:"wu",
    obj1:obj1
};

obj2.obj1.example(); //karine

当函数引用有上下文对象时,函数里的this绑定在这个上下文对象上。
但是这种情况可能会出现“隐式丢失”,即丢失绑定的对象,应用默认绑定(全局对象/undefinded):

1. 将函数引用赋值给一个新变量,然后执行。
var obj3 = obj1.example; 
obj3(); //全局or undefined
实际上obj3引用的是example函数本身,因此this还是指向全局/undefined

2. 或者是调用回调函数的函数修改了this
function test(fn){
    fn();
}
test(obj1.example);  //全局or undefined
实际上,参数传递也是一种赋值,只不过是隐式赋值。所以道理同上。也会指向全局。

3.显式绑定
(在对象上强制调用函数,可以使用call(对象)、apply(对象)方法,会把形参里的对象绑定到this上,接着在调用函数时指定这个this。如果传入的参数为原始值(string、number、boolean),该原始值会自动转为其对象形式(new String()、new Number()、new Boolean()),这通常被称为“装箱”)

function example(){
    console.log(this.a);
}
var obj = {
    a:"karine"
};
example.call(obj); //karine

example.apply(obj);//karine

var o = example.bind(obj);
o(); //karine
“硬绑定”bind 的this不会被显示or隐式绑定修改。

4.new绑定
在js中,构造函数只是使用new操作符是被调用的普通函数而已,不属于某个类,也不会实例化一个类。使用new调用函数时,会自动执行以下操作:

1.创建一个全新的对象
2.该对象会被执行[[prototype]]连接
3.该对象会绑定到函数调用的this
4.若函数没有返回其他对象,new表达式中的函数调用会自动返回该对象。
function examplea){
    this.a=a;
}
var obj4 = new example(“karine”);
console.log(obj4.a);//“karine”

【四种this绑定方法的优先级】

new绑定 > 显式绑定 >隐式绑定 > 默认绑定

【例外】:
1.null、undefined作为this的绑定对象传入call、apply、bind时,会被忽略,实际应用的是默认绑定(全局对象)

function example(){
    console.log("a:"+this.a);
}
var a = "karine";
example.call(null); //a:karine

传入null的情况:
(1)使用apply来展开一个数组
(2)使用bind进行柯里化

function example(a,b){
    console.log("a:"+a+“,b:”+b);
}
//使用apply来展开一个数组
example.apply(null,[2,3]); //a:2,b:3
//使用bind进行柯里化(预先设置一些参数)
var o = example.bind(null,2); 
o(3);//a:2,b:3

若想避免不必要的this绑定,可以使用ES6的…操作符来代替apply()

example.apply(null,[2,3]) ===  example(…[2,3])

为了安全,若想忽略this绑定,可以给this传入一个空的非委托对象Φ (“Object.create(null)”),不会产生任何副作用,因为this会限制在这个空对象中,不会对全局对象产生任何影响。
Object.create(null)和{}很像,但是不会创建object.prototype,所以比{}“更空”

var Φ = Object.create(null);
example.apply(Φ,[2,3]); //a:2,b:3
var o = example.bind(Φ,2); 
o(3); //a:2,b:3

2.间接引用(经常在赋值时发生),会应用默认绑定。

3.ES6中 “=>”定义的箭头函数不使用this的四种标准规则。而是根据外层作用域来决定this。箭头函数的绑定无法修改。

function example(){
    return(a)=>{
        console.log(this.a);
    }
}
var obj1={a:2};
var obj2={a:3};
var test = example.call(obj1);
test.call(obj2); //2

由于example()的this绑定到obj1,所以test中引用箭头函数的this也会绑定到obj1。


总结】:

1.找到this的调用位置
2.根据四条规则判定this的绑定对象:

(1)new:绑定到新创建的对象
(2)call、apply、bind:绑定到指定的对象
(3)上下文对象:绑定到该上下文对象
(4)默认:全局或者undefined(严格模式下)

3.箭头函数根据当前的词法作用域来决定this
4.有些调用可能无意中使用默认规则,可以使用一个空对象Object.create(null)忽略this绑定,来保护全局对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值