浅谈javascirpt this机制
JS中的this到底是谁无疑是困扰很多新手的问题,在我们平时的编程中,很难绕开this的使用,它可以极大的方便我们编程。其实当我们熟悉了this的绑定规则之后我们可以很轻松的驾驭它。这里我们就来探讨一下this的绑定规则。首先我们要知道什么是this,我们其实可以把this当作成一个对象,它是函数执行时动态添加的,所以我们写函数时也不知道this是谁,只有他执行是才知道,但是它是谁到底也还是我们控制的。
一.绑定规则
1 .构造函数绑定
在构造函数中,我们可以理解成我们在构造函数顶部创建了一个this对象 然后给this添加属性(这里只是比喻,内部的机制远不
止此)
function You(name,age) { var this = new Object();(//隐形的假传看不见!) this.name = name;//this添加属性name this.age = age//this添加属性age return this(//隐形的假传看不见!) //将this返回(这只是为了让我们更好理解,内部处理不止如此) }这是一个You构造函数,在函数内部我们创建了一个this对象,并给它添加了name 和 age 属性在分别赋值,这和我们平时给对
象添加属性的形式是一样的,当我们使用new操作符,就会将这个this返回(再次强调只为更好理解)。
function You(name,age) { this.name = name; this.age = age } var person = new You("jack","20"); console.log(person.name);//"jack console.log(person.age);//"20"这里new使You返回了this对象,创建了一个实例并赋值给person。这里我们要注意一个点,就是构造函数内部使用了this,就不要
出现return语句,因为我们用new操作符实例对象会隐式的返回this对象,如果我们在构造函数内部使用了returen语句 new操作符将
不在返回this,而是返回你定义的那个return语句。
function You(name,age) { this.name = name; this.age = age; return { name:"haha", age:"100" } } var person = new You("jack","20"); console.log(person.name);// haha console.log(person.age);// 100这里可以看到如果我们在构造函数内部使用了return,那么new将返回这个return之后的内容而不是this。
当然这这是构造函数中的运用,我们平时运用它的地方远不止此!我们经常在方法中使用this,可以根据动态生成this更好的访问
对象属性,让我们的代码更“先进”。
2.普通函数绑定
①默认绑定
var name = "windows"; function tom() { var name = "tom"; function say() { console.log(this.name) } } say();//"windows"咦!这里为啥那么是windows 我刚开是学的时候十分困惑 , 我们觉得 this绑定在tom的作用域才合理,因为say在tom中定义的。有人会说虽然在tom中定义但是实在全局作用域下运行的,this是动态绑定的所以是windows。那我们再看下面这个例子
var name = "windows";在 function tom() { var name = "tom"; var say = function () { console.log(this.name) }; say() } tom();//windows我当时刚接触的时候十分困惑,因为say在Tom内部执行,this不因该绑定tom的执行环境嘛,为什么还是windows?后来得知这是设计上的失误(内心崩溃)。所以函数平常的执行,无任何修饰,this全是默认绑定在windows上。这一点我们当成概念记住,无从解释。我们就记得函数执行默认就是把this绑定给windows。
②隐式绑定
我们平时还会用到”对象.方法“的方式访问函数。这时this就会发生变化了这里的this会指向调用它的那个对象。例如
function say() { console.log(this.name) } var jack={ name:"jack", say:say }; jack.say();//jack这里this就会隐式的绑定给调用方法的那个对象,只会在调用它的那个对象里找name,如果没有就会返回undefined,无论windows中有没有绑定,可以说此时this的上下文就只在这个对象之中。
③强制绑定
我们知道函数都有call和apply方法,这两个函数的第一个参数接收一个对象,当函数调用这两个方法时就会将this绑定给这个对象。
var name ="windows"; function say() { console.log(this.name) } var jack={ name:'jack' }; say.call(jack);//jack这里的this就会绑定到jack对象上面,因为call方法就是绑定this的值,从而让say中的this绑定到jack上,而不是默认的windows对象上面。
以上就是全部的this绑定规则,这时我们问题就来了,既然有这么多绑定那么他们同时出现怎么算。这就要进行优先级排序了。
二 优先级比较
那么 默认绑定 隐式绑定 强制绑定的优先级是什么呢?同时出现this会绑定谁呢?
前面我们已经看到了 隐式绑定和强制绑定的优先级都是大于默认绑定的,这里我们就只需要判断它们俩的优先级,还是看代码。
var name ="windows"; var jack={ name:'jack', say:say }; var tom={ name:"tom" }; jack.say.call(tom);//tom一个例子胜过千言万语,这个例子说明了一切。这里同时有三种绑定,默认全局有name="windows",调用时通过jack.say的方调调用(隐式绑定),有通过函数的call方法强制绑定tom 最后的就结果是tom。
这里可以看出强制绑定的优先级是高于隐式绑定的。所有优先级强制绑定 > 隐式绑定 > 默认绑定。
三 ES6中的箭头函数
const say = (name)=>{ console.log(name) }; say("jack")这就是箭头函数,具体用法可以看ES6入门,哪里有详细解答,这里我们只讨论其中的this。
我们知道函数中this默认是绑定到windows上的。可是在箭头函数中就不一样了。
let name = "windows"; function foo() { setTimeout(() => { console.log( this.name); }, 50); } foo.call({ name: "foo" });//"foo"我们看到在foo函数中定义了一个定时器,里面有一个箭头函数,我们知道函数默认都是绑定到windows上的那么,定时器执行,应该将其中的this绑定到windows上,从而输出“windows"。但是最后结果是"foo"。call让foo的this指向了{name:"foo"}对象,此时箭头函数里的this也指向了这个对象(因为输出是"foo")。
这里我们就可以看出来了:箭头函数的this不在遵循以前的函数绑定,而是绑定在定义时所在的对象。他是固定的。以前的this是动态生成,而箭头函数中的this在我们书写的时候就决定他的指向。