原文地址javascript
一般所说的:若是是全局环境中,this指向全局对象,若是是对象的方法,这this指向这个对象。html
例子1:java
var foo = { bar: function() { console.log(this) } } foo.bar(); (foo.bar)(); (foo.bar = foo.bar)(); (false || foo.bar)(); (foo.bar, foo.bar)();
例子1前二者为foo,后面都是全局对象。后三者并无指向foo。因此咱们上面的一般说法不精确。git
在全局环境中,this指向全局对象。而在普通函数调用中,this是由激活上下文的调用者提供,即调用这个函数的父做用域,以及函数调用的语法形式,决定了this的值,这是一个动态可变的值。github
例子2:编程
var foo = { bar: function() { console.log(this) console.log(this === foo) } } foo.bar() // foo, true var fn = foo.bar console.log(fn === foo.bar) // true fn() // global, false
例子2中,第一次调用指向foo,把foo.bar
赋值给fn以后,this没有指向foo。是什么致使this指向的变化呢?数组
this
是执行上下文的一个属性:缓存
activeExecutionContext = { VO: {...}, this: thisValue }
在普通函数调用中,this是由激活上下文的调用者提供,即调用这个函数的父做用域,函数调用的语法形式,决定了this的值,这是一个动态可变的值。闭包
为何会引发这个差别呢?
由于引用类型的不一样处理,是否会获取真实的值,所致使的。app
引用类型存在形式:
1 标识符(变量名,函数名,函数参数名,全局对象属性名)
2 属性访问器(foo.bar(); foo['bar']()
, 点标记法;能够动态设置属性名的方括号[]
)
为了从引用类型中获取真实的值,存在相似getValue
的方法。而函数上下文的规则是,函数上下文中this由调用者提供,并由调用形式决定。若是调用的圆括号左侧是一个引用类型,this为这个引用类型,若是是非引用类型,这为null,但为null无心义,被隐式转化为全局对象。
this是一个指针,便于代码的更为简洁地复用。
// 无this function upper(context) { return context.name.toUpperCase() } function speak(context) { var greeting = "Hello, I'm " + upper(context) console.log(greeting) } var me = { name: 'm' } var you = { name: 'y' } speak(me) // 利用this function upper() { return this.name.toUpperCase() } function speak() { var greeting = "Hello, I'm " + upper.call(this) console.log(greeting) } speak.call(me)
这里this能够简化上下文对象的传递。其余OPP语言中this关键字和OPP密切相关,通常是引用刚建立的对象,但在ECMAScript中,this只限于引用建立过的对象,this的指向和函数调用形式有关,不必定引用类型调用就指向引用类型。
1 构造函数中的this
function C() { console.log(this) this.x = 10 } var a = new C() console.log(a.x);
new操做符会调用函数的内部的Construct方法,建立对象,以后调用函数的Call方法,把新建立对象做为this值。
2 调用函数时call与apply设置this的值
var b = 10 function a(c) { console.log(this.b) console.log(c) } a(20) a.call({b: 20}, 30) a.apply({b: 20}, [40])
call,apply,bind皆为动态的改变this指针的方法。其中call和apply是当Object没有某个方法,可是其它对象有,能够借助call和apply改变this的指向,调用其它对象的方法。bind为绑定this为某个对象。
典型的应用:
将类数组元素转化为数组:Array.prototype.slice.apply(document.getElementsByTagName('*'))
检查类型:
function isArray(obj) { return Object.prototpye.toString.call(obj) === '[object Array]' }
箭头函数则与前三者不一样。
If kind is Arrow, set the [[ThisModel]]
internal slot of F to lexical.If the
value is "lexical", this is an ArrowFunction and does not have a local this
If thisModel is lexical, return NormalCompletion(undefined).
箭头函数没有本身的this绑定,同时在函数执行时绑定this会被直接忽略。其中this老是指向定义时所在的对象,而不是运行时所在的对象。即箭头函数的this值是lexical
scope 的this值。这一特性使得箭头函数在React中的render函数中使用起来很方便。
function foo() { setTimeout(() => { console.log('id: ', this.id) }, 100) } var id = 0 foo.call({id: 42}) // 容易误解的地方 // {id: 42} // 是箭头函数定义所在的对象仍是运行时所在的对象。因为箭头函数位于foo函数内部,只有foo函数运行以后他才会生成,因此foo运行时所在的对象,即箭头函数定义所在的对象。
var f = () => 5; // 近似等价于 var f = function() {return 5;}.bind(this);
综上,call,apply,bind使得JavaScript具备动态改变this的特性,而箭头函数使得JavaScript具备固定this的指向的特性。一动一静,相得益彰。
::
this.x = 0 let module = { x: 1, getX: function() { console.log(this.x) } } module.getX() let get = module.getX get() // 0 let boundGetX = get.bind(module) boundGetX() // 1 let ES7boundGetx = module::get ES7boundGetx() // 1
class P { foo() { console.log('P.foo') } } class C extends P { foo() { super.foo() } } var c1 = new C() c1.foo() // P.foo var D = { foo: function() { console.log('D.foo') } } var E = { foo: C.prototype.foo } Object.setPrototypeOf(E, D) E.foo() // P.foo
可见super的绑定是静态绑定,建立时即完成绑定。因此E委托了D,但并不能调用到D.foo()
,相似于箭头的函数的this绑定。
链式调用的实现;
function Constructor() { this.art = 0 } Constructor.prototype.fn_0 = function() { console.log('0') return this; } Constructor.prototype.fn_1 = function() { console.log('1') return this; } new Constructor().fn_0().fn_1()
调用的方法返回this便可。
end()
的实现
function end() { return this.prevObject || this.constructor(null) } // 设置preObject的函数 function pushStack( ele ) { // Build a new jQuery macthed element set var ret = jQuery.merge( this.constructor(), elems); ret.prevObject = this // ret.pervObject 设置为当前jQuery对象引用 ret.context = this.context return ret; }
pushStack函数在不少涉及DOM操做的函数都有调用,用于缓存了当前的this。因为只存储当前,因此这里只须要一个preObject便可,无需放在一个数组里。
this是JavaScript特性之一,具备脚本语言的动态特性,带来不少便捷,同时因为super和箭头函数的特性,使得this具备了静态的特性,在这两种状况下,this是固定且没法改变的。其利与弊都是this的灵活,双刃剑。因此才有了ES2015中super和箭头函数的固定this的特性。
this可被从新赋值么?(不能,this是保留字)
1 call参数为null时,this的指向
function a() { console.log(this) } a.call(null)
2 调用形式对this的影响
var foo = { bar: function() { console.log(this) } } foo.bar(); (foo.bar)(); (foo.bar = foo.bar)(); (false || foo.bar)(); (foo.bar, foo.bar)();
《你所不知道的JavaScript(上卷)》