1.this的最终指向实质上是那个调用它的对象。
1、在对象方法中, this 指向调用它所在方法的对象。
(如果函数调用前存在多个对象,this指向距离调用自己最近的对象)
2、单独使用 this,它指向全局(Global)对象。(window)
3、函数使用中,this 指向函数的所属者。(调用时的事件对象)
4、严格模式下函数是没有绑定到 this 上,这时候 this 是 undefined。
5、在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素。
6、apply 和 call 允许切换函数执行的上下文环境(context),即 this 绑定的对象,可以将 this 引用到任何对象。
2.显式绑定(改变this指向)
call / apply
Function.call(obj/this,args1,args2,args3...)
obj:这个对象,将替代Function里面的this
从第二个参数开始,是一个参数列表
Function.apply(obj/this,[args1,args2,args3,...]) apply方法接收两个参数
obj:这个对象,将替代Function里面的this
args: apply中第二个参数是一个类数组,他可以作为参数传给Function
Function.bind(obj/this,args1,args2,args3...)()
obj:这个对象,将替代Function里面的this
从第二个参数开始,bind 除了返回是函数以外,后续的参数可以作为函数的参数传入到函数中,
bind()方法和call apply 相似,也可以改变this的指向。
注意:
如果在使用call之类的方法改变this指向时,指向参数提供的是null或者undefined,那么 this 将指向全局对象
三者区别
a.call、apply与bind都用于改变this绑定,但call、apply在改变this指向的同时还会执行函数,而bind在改变
this后是返回一个全新的boundFcuntion绑定函数,这也是为什么上方例子中bind后还加了一对括号 ()的原因。
b.bind属于硬绑定,返回的 boundFunction 的 this 指向无法再次通过bind、apply或 call 修改;
call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。
c.call与apply功能完全相同,唯一不同的是call方法传递函数调用形参是以散列形式,而apply方法的形参是
一个数组。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。
当我们调用一个函数时,我们习惯称之为函数调用,函数处于一个被动的状态;而call与apply让函数从被动变主动,函数能主动选择自己的上下文,所以这种写法我们又称之为函数应用
call,apply方法除了可以改变this指向外,还可以用于传递参数,因为没有对象去调用Math的这个方法,所以直接传递null过去:ex:
//Math方法中
arrH=[...];
var minH = Math.min.apply(null, arrH)
var obj = {
abc: '伍天锡'
};
var abc = '伍云召';
function wu() {
console.log(this.abc);
}
wu.call(obj);//伍天锡
wu.apply(obj);//伍天锡
wu.bind(obj)();//伍天锡
3.隐式丢失
原理:将函数调用作为参数传递或者作为变量的赋值
//参数传递
var name = '行星飞行';
let obj = {
name: '听风是风',
fn: function () {
console.log(this.name);
}
};
function fn1(param) {
param();
};
fn1(obj.fn);//行星飞行
上边的行星飞行例子类似于
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
//虽然obj.foo和foo指向同一个函数,但是执行结果并不一样
//这种差异的原因,就在于函数体内部使用了this关键字。很多教科书会告诉你,
//this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,
//所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。
//所以,两者的运行结果不一样
//隐式丢失的问题是变量赋值,其实本质上与传参相同
var name = '行星飞行';
let obj = {
name: '听风是风',
fn: function () {
console.log(this.name);
}
};
let fn1 = obj.fn;
fn1(); //行星飞行
4.作用域链与原型链的区别:
当访问一个变量时,解释器会先在当前作用域查找标识符,如果没有找到就去父作用域找,作用域链顶端是全局对象window,如果window都没有这个变量则报错。
当在对象上访问某属性时,首选i会查找当前对象,如果没有就顺着原型链往上找,原型链顶端是null,如果全程都没找到则返一个undefined,而不是报错。