我正在参加「掘金·启航计划」点击活动查看详情
this
搞明白this四种绑定规则以及优先级也就明白了this指向了。
new > 显式绑定 > 隐式绑定 > 默认绑定
默认绑定
当独立函数调用时,不管是否在调用栈中,this都指向全局对象(浏览器中为window)
严格模式下不能将全局对象用于默认绑定
// "use strict" 严格模式下this访问不到local:Uncaught TypeError
var local = 'earth'
function gps() {
console.log(this.local);
}
function locate() {
var local = 'China'
gps()
}
locate() // earth local:我有的不仅仅是中国,而是世界(haahaha)
复制代码
进一步解释独立函数调用。
function universe() {
console.log(this); // Window
earth()
}
function earth() {
console.log(this); // Window
china()
}
function china() {
console.log(this); // Window
}
universe() // 三个打印都是window对象。window: 只要你落单了,就要和我绑定。
复制代码
无论是这种链式的函数调用,亦或是作为参数传入函数中直接调用,还有以下这种情况
。对象中的方法作为参数传入另一个函数中直接调用,相当于把堆里面的那个函数放在了另一个函数里单独调用,而并没有与该对象相关联,当该对象调用时也就是this指向了这个对象了。
function china(fn) {
fn()
}
var chinese = {
name: "赵文卓",
kungfu: '踢飞刀',
skill: function() {
console.log(this.kungfu);
}
}
china(chinese.skill) // undefined 不可能全中国的人都会踢飞刀
chinese.skill() // 踢飞刀 但是赵文卓一定可以
复制代码
隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
对象属性引用链中只有最后一层在调用位置中起作用。
要求:对象内部必须包含一个指向函数的属性,该对象可通过这个属性间接引用函数。
function foo() {
let name = '乾隆'
console.log(this.name);
}
let obj2 = {
name: '雍正',
next: foo
}
let obj1 = {
name: '康熙',
next: obj2
}
obj1.next.next() // 雍正 看到这里是不是以为康熙的下一任的下一任怎么不是乾隆,害,这里毕竟是代码。
复制代码
被调用的函数中的this会绑定到最靠近的那层的上下文对象。
隐式丢失:指隐式绑定的函数丢失绑定对象,也就是说它会应用默认绑定,从而默认绑定到全局或者undefined。
var woman = '紫薇'
var emperor = { // 皇帝
woman: '夏雨荷',
meet: function() {
console.log(this.woman);
}
}
var xyz = emperor.meet // 这里xyz拿到了meet函数的引用,也就是meet函数本身,所以也就没了emperor的上下文了
xyz() // 紫薇 这里独立函数调用 // 这里面的剧情自己可以理出来了(*^﹏^*)
复制代码
回调函数里发生的隐式丢失,这里其实就是函数作为参数,然后再setTimeout函数里独立运行。
var woman = '紫薇'
var emperor = { // 皇帝
woman: '夏雨荷',
meet: function() {
console.log(this.woman);
}
}
setTimeout(emperor.meet, 3000); // 紫薇 三秒后皇帝见到了紫薇
复制代码
显式绑定
通过
call
和apply
,将this绑定到该对象。call和apply区别在于第二个参数不同,call是参数列表,apply是数组。
function getMoney() {
console.log(this.card);
}
var wife = {
card: 1000,
getMoney: getMoney
}
var husband = {
card: 999999
}
wife.getMoney() // 1000
wife.getMoney.call(husband) // 999999 双十一要到了,老婆准备去霍霍老公的卡。
复制代码
从这里也能看出显式绑定和隐式绑定的优先级:显式绑定>隐式绑定
new绑定
new
关键字会进行如下的操作:
创建一个新的对象({});
为这个新的对象添加__proto__属性,将该属性指向构造函数的原型对象;
将新创建的对象作为
this
的上下文;如果该函数没有返回对象,则返回
this
。
function Father(y) {
this.y = y;
}
var son = new Father('Y'); // son这个新创建的对象作为了this的上下文,也就是构造函数里的this添加的属性都是往son里添加的
console.log(son.y); // Y 父亲的Y染色体决定了你是个son
复制代码
补充:箭头函数没有this,它所采用的是词法作用域 。
剧情讲完了,这里就来上一段快手的面试题吧。如何输出456?
var name = '123';
var obj = {
name: '456',
getName: function () {
function printName() {
console.log(this.name);
}
printName();
}
}
obj.getName(); // 123
复制代码
既然讲到了箭头函数部分,想必你已经想到了要把某个函数改成箭头函数吧。还有种方法就是用到call了。
var name = '123';
var obj = {
name: '456',
getName: function () {
let printName = () => {
console.log(this.name);
}
printName();
}
}
obj.getName(); // 456
复制代码
此处getName函数中this的指向也就是定义时所在的对象,而非像前面那些运行时才绑定this。
优先级
上面显式绑定的例子已经对比出了显式绑定>隐式绑定,毫无疑问,默认绑定的优先级是最低 的,现在就来看看new绑定和隐式绑定吧。
function father(x) {
this.x = x;
}
var daughter = {}
var wife = {
x: 'X',
father:father
}
// 丈夫是个变种人,由于长时间和丈夫待在一起,wife基因发生了突变
wife.father('Y')
console.log(wife.x); // Y
// call技术实在太强大了,强行生下一个女儿 // 显式 > 隐式
wife.father.call(daughter, 'X')
console.log(daughter.x); // X
// 没想到new也很强大,又喜提千金 // new > 隐式
var daughter2 = new wife.father('X')
console.log(wife.x); // Y
console.log(daughter2.x); // X
复制代码
这样一来就差new绑定和显式绑定谁更强了。
function control(money) {
this.money = money
}
var husband = {}
// 妻子掌握了丈夫的微信余额
var wife = control.bind(husband)
// 妻子使丈夫余额有了500
wife(500)
console.log(husband.money); // 500
// 儿子诞生,余额1000
var son = new wife(1000)
// 生了儿子降低生活费,营养费
wife(200)
console.log(husband.money); // 200
console.log(son.money); // 1000
复制代码
在上面的wife的this绑定的了husband的上下文,但是在new的过程中this被修改了,所以new>显式。但是在当再次通过wife去改变husband余额仍然有效,这难道说new 不比显式的优先级高。具体原理感兴趣的可以去《你不知道的JavaScript》上卷里面去了解。