You Don't Know JS: this & Object Prototypes( 第2章 this)
this
is a binding made for each function invocation, based entirely on its call-site (how the function is called).
this是为函数被引用而创建的绑定!完全地基于函数如何被调用/函数的call-site!
Call-site
:the location in code where a function is called
call-stack: 调用函数的堆栈。(函数的调用链条,函数的call-site的移动形成了stack。)
The call-site is in the invocation before the currently executing function.
function baz() {
// call-stack is: `baz`
// so, our call-site is in the global scope console.log( "baz" );
bar(); // <-- 下一个调用位置是'bar'
} function bar() {
// call-stack is: `baz` -> `bar`
// so, 我们的call-site是在'baz'内。 console.log( "bar" );
foo(); // <-- call-site for `foo`
} function foo() {
// call-stack is: `baz` -> `bar` -> `foo`
// so, our call-site is in `bar` console.log( "foo" );
} baz(); // <-- call-site for `baz`
在浏览器inspector中,可以使用debugger工具观察call-site。
function baz() {
console.log(this);
console.log( "baz" );
bar(); // <-- call-site for `bar`
} function bar() {
console.log(this)
console.log( "bar" );
foo(); // <-- call-site for `foo`
} function foo() {
console.log(this);
console.log( "foo" );
} baz();
最后的结果是3个this都指向window对象。
Nothing But Rules
在函数执行期间,call-site如何决定this指向哪里?
有:4条法则!及这4条法则的优先级。
default Binding
第一条法则来自最常用的函数调用: 独立的函数引用。(没有其他法则影响的函数)
function foo() {
console.log( this.a );
} var a = 2; foo(); // 2 this指向全局对象,变量a是全局对象的属性。 如果是严格模式,this指向undefined!
第一条:this绑定完全基于call-site,非严格模式下,全局对象是默认的绑定对象。
注意:不要混用严格模式和非严格模式!
Implicit Binding
第2条:如果call-site有一个context对象,也涉及一个拥有或包含的对象。
function foo() {
console.log( this.a );
} var obj = {
a: 2,
foo: foo
}; obj.foo(); // 2 因为obj就foo()调用的this, 所以this.a等同于obj.a 当有一个content object 拥有一个函数引用时,
Implicit Binding Rule 就是那个对象应当拥有这个函数调用的this绑定
⚠️:Only the top/last level of an object property reference chain matters to the call-site.
也就是说假如有: obj1.obj2.obj3.foo(), 调用foo()函数的this绑定在obj3上。
Implicitly Lost
当一个Implicit bound 函数丢弃了context对象的binding, 就会使用默认binding。或用全局对象,或undefined(use strict)。
function foo() {
console.log( this.a );
} var obj = {
a: 2,
foo: foo
}; var bar = obj.foo; // function reference/alias! var a = "oops, global"; // `a` also property on global object bar(); // "oops, global" obj.foo
结果是:
ƒ foo() {
console.log( this.a );
}
bar //变量bar的值就是函数foo, 因为bar被分配了obj.foo
结果是
ƒ foo() {
console.log( this.a );
} 所以: this指向全局对象window, this.a就是 window.a
另一个例子:参数传递
其实就是把obj.foo的值,分配给fn参数。等同于在doFoo函数内,声明了变量:var fn = obj.foo;
function foo() {
console.log( this.a );
} function doFoo(fn) {
// `fn` is just another reference to `foo` fn(); // <-- call-site!
} var obj = {
a: 2,
foo: foo
}; var a = "oops, global"; // `a` also property on global object doFoo( obj.foo ); // "oops, global" 看到了吧: 结果this绑定的是全局对象。this.a就是window.a
JavaScript内建函数也同样:
function foo() {
console.log( this.a );
} var obj = {
a: 2,
foo: foo
}; var a = "oops, global"; // `a` also property on global object setTimeout( obj.foo, 100 ); // "oops, global" setTimeout()函数相当于: function MySetTimeout(fn) {
//0.1秒后;
fn();
} MySetTimeout( obj.foo )
函数回调丢失它们的this绑定是很常见的。
还有一类方式,是主动改变this:
Event handlers就会强制你的回调有一个this指向DOM中被激活的元素。
如果有一种方法可以固定住this,就好了!当然有了,见⬇️,明确的绑定!
Explicit Binding
这是第3个rule:使用call()方法,apply()方法。第一个参数是一个对象的话,这个对象就绑定了this。
function foo() {
console.log( this.a );
} var obj = {
a: 2
}; foo.call( obj ); //
⚠️,call(..),apply(..)可接受多个参数,它们的用途这里不谈!
不幸的是,明确绑定,不能解决一个函数丢失它想要的this绑定的问题。
Hard Binding(explict and strong)
我的理解就是再加一层函数表达式给一个声明的变量。这个变量的值就是函数。
apply()和call()的功能一样,区别就是apply可以接受array作为参数。
function foo(something) {
console.log( this.a, something );
return this.a + something;
} var obj = {
a: 2
}; var bar = function() {
return foo.apply( obj, arguments );
}; var b = bar( 3 ); // 2 3
console.log( b ); //
API Call "Contexts"
许多库的函数,和许多新的内建函数,提供了可选参数,叫做'context'。
context即一个上下文环境。一般传入一个对象参数。
用途就是确保你的回调函数使用一个指定的this关键字 。
function foo(el) {
console.log( el, this.id );
} var obj = {
id: "awesome"
}; // use `obj` as `this` for `foo(..)` calls
[1, 2, 3].forEach( foo, obj );
// 1 awesome 2 awesome 3 awesome [1, 2, 3].forEach( foo );
//返回的1 undefined, 2 undefined, 3 undefined
//因为此时this是window对象。
new
Binding
第四条法则:
先看一个误区:JS的new操作符号,和这个代码: sth = new MyClass(),的使用类似传统的类语言。
但是:实际上在JS中,并没有class-oriented功能暗示和new相关。
constructor: 它就是一个函数,和new操作符一起在被调用时运行。和类无关也不实例化类!就是一个标准的函数。
如 new Number(..) ,
没有constructor function,只有construction calls of functions
当一个函数前面有一个new, 会自动做以下事情:
- a brand new object is created
- the newly constructed object is
[[Prototype]]
-linked, - the newly constructed object is set as the
this
binding for that function call - unless the function returns its own alternate object, the
new
-invoked function call will automatically return the newly constructed object.
例子:
function foo(a) {
this.a = a;
} var bar = new foo( 2 );
console.log( bar.a ); // 2
bar
foo {a: 2}
a: 2
__proto__: Object
当调用foo()函数,前面加一个new的时候:
- 1. 新建一个对象foo{ .. } 。
- 2.(这里不讨论prototype)
- 3. 新建的对象绑定了this关键字, foo.a = 2
- 4. 默认自动返回新建的对象,给变量var。
总结: 4条rule:
- default binding
- implicit binding
- explicit binding: 使用call(), apply()
- new binding
使用方法:先找到call-site,然后检查符合哪条rule。
Everything In Order
是否在一个call-site,有多个rule生效?
答案:rule也是有优先级顺序的!!
- 很清楚,第一条default binding rule是最低优先级priority
- Explicit binding rule 高于 Implicit binding rule。
- new rule 高于 Implicit binding rule
function foo(something) {
this.a = something;
} var obj1 = {
foo: foo
}; var obj2 = {}; obj1.foo( 2 );
console.log( obj1.a ); // obj1.foo.call( obj2, 3 );
console.log( obj2.a ); //
明确使用call方法,即Explicit rule 优先于 Implicit rule var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4
new rule 优先于 Implicit rule
(一大坨没有读!)
Determining this
- 如果函数调用前面加上了new, 则this是新的constructed object。
- 使用了call/apply方法的函数, this就是明确指定的对象。
- var bar = foo.call( obj2 )
- 如果函数被一个context调用。 this就是context object。
- var bar = obj1.foo()
- 其他情况,默认this,是default binding。如果是严格模式,pick undefined,否则用全局对象。
但还没完。。。
Binding Exceptions
总有一些例外。
Ignored this
如果你传递null, undefined作为this绑定参数来call, apply, 或者bind, 那些值会被忽略,使用default binding rule。
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
} // spreading out array as parameters
foo.apply( null, [2, 3] ); // a:2, b:3 // currying with `bind(..)`
var bar = foo.bind( null, 2 );
bar( 3 ); // a:2, b:3
Safer this
我的简单理解,创建一个Object.create(null), 替代null。
Object.create(null)没有委托到Object.prototype,所以更干净。
Indirection
you can (intentionally or not!) create "indirect references" to functions, and in those cases, when that function reference is invoked, the default binding rule also applies.
非直接的引用,使用default binding rule!!
除了以下这个案例使用“立即函数执行”,我不知道其他的用法!
function foo() {
console.log( this.a );
} var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 }; o.foo(); //
(p.foo = o.foo)(); // 2
call-site是foo(),不是p.foo(),也不是o.foo(), 所以this就是全局对象。
Softening Binding(没看!)
Lexical this
箭头函数fat arrow, 不使用标准的4条rules。
箭头函数的this binding来自enclosing scope!
function foo() {
// return an arrow function
return (a) => {
// `this` here is lexically adopted from `foo()`
console.log( this.a );
};
} var obj1 = {
a: 2
}; var obj2 = {
a: 3
}; var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, not 3!
当foo()函数被调用时, 箭头函数在foo()内被创建,箭头函数会捕捉到foo()的this. 这个this就是箭头函数的Lexical scope。
因为foo()的this绑定到了obj1 , 所以 bar的this也绑定到了obj1。
箭头函数的Lexical binding不会被重写!!
箭头函数常用的案例是event handlers或者timers.
function foo() {
setTimeout(() => {
// `this` here is lexically adopted from `foo()`
console.log( this.a );
},100);
} var obj = {
a: 2
}; foo.call( obj ); // 2
在没有箭头函数的时候,会使用var self = this来得到
function foo() {
var self = this; // lexical capture of `this`
setTimeout( function(){
console.log( self.a );
}, 100 );
} var obj = {
a: 2
}; foo.call( obj ); //
Review (TL;DR)
4条rule
Called with
new
? Use the newly constructed object.Called with
call
orapply
(orbind
)? Use the specified object.Called with a context object owning the call? Use that context object.
Default:
undefined
instrict mode
, global object otherwise.
有例外情况:
Ignored
this, 使用明确绑定(call,apply,bind)时,如果传入的参数值时undefined,null则使用default rule。
- 非直接使用。看上面的案例
API Call "Contexts"。 许多方法可以传入一个context Object,这个对象就是this.
- 软binding(未看)
许多库的函数,和许多新的内建函数,提供了可选参数,叫做'conte
箭头函数,的this可以得到箭头函数被定义/创建时的Lexical scope。
You Don't Know JS: this & Object Prototypes( 第2章 this)的更多相关文章
- You Don't Know JS: this & Object Prototypes( 第5章 Prototypes)
qu上章提到过[[prototype]] chain, 本章详细分析 ⚠️所有试图模仿类复制的行为,如上章提到的mixins的变种,完全规避了[[Prototype]] chain机制,本章会谈到这方 ...
- You Don't Know JS: this & Object Prototypes( 第4章 Mixing "Class" Objects)
本章移到“Object oriented programming”和"classes". 看‘class orientation‘ 的设计模式: instantiation, in ...
- You Don't Know JS: this & Object Prototypes( 第3章 对象)
前2章探索了this绑定指向不同的对象需要函数引用的call-site. 但是什么是对象,为什么我们需要指向它们? 本章探索细节. Syntax the rules that describe ho ...
- You Don't Know JS: this & Object Prototypes( 第一章 this or That?)
Foreword this 关键字和prototypes 他们是用JS编程的基础.没有他们创建复杂的JS程序是不可能的. 我敢说大量的web developers从没有建立过JS Object,仅仅对 ...
- You Don't Know JS: this & Object Prototypes (第6章 Behavior Delegation)附加的ES6 class未读
本章深挖原型机制. [[Prototype]]比类更直接和简单! https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%2 ...
- JS的Object漫想:从现象到“本质”
转自:http://zzy603.iteye.com/blog/973649 写的挺好,用于记录,把对象分成概念的Object(var f={})和 类的Object(function F(){}) ...
- Javascript中Function,Object,Prototypes,__proto__等概念详解
http://anykoro.sinaapp.com/2012/01/31/javascript%E4%B8%ADfunctionobjectprototypes__proto__%E7%AD%89% ...
- js中Object.defineProperty()和defineProperties()
在介绍js中Object.defineProperty()和defineProperties()之前,我们了解下js中对象两种属性的类型:数据属性和访问器属性. 数据属性 数据属性包含一个数据的位置, ...
- js 之 object
js之object 对象 ECMA-262 把对象(object)定义为“属性的无序集合,每个属性存放一个原始值.对象或函数”.严格来说,这意味着对象是无特定顺序的值的数组. 尽管 ECMAScrip ...
随机推荐
- 【转】java提高篇之理解java的三大特性——多态
面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承 ...
- LOIC
Pre: http://sourceforge.net/projects/loic Getting the Software To DDos, first your going to have to ...
- linux命令之crontab定时执行任务【转】
本文转载自:https://www.cnblogs.com/coffy/p/5608095.html 一.crond简介 crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护 ...
- linux内核中的vgaarb是什么?
答: vga仲裁器(vga arbiter),是内核中的一个模块
- 237. 程序自动分析 【map+并查集】
程序自动分析 描述 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足. 考虑一个约束满足问题的简化版本:假设x1,x2,x3,…x1,x2,x3,…代表程序中出现的变量,给定n个形 ...
- Cygwin、MinG、MSys区别与联系(转)
转自:https://www.biaodianfu.com/cygwin-ming-msys.html 什么是Cygwin? Cygwin,原Cygnus出品(已被红帽收购),目前是RedHat名下的 ...
- smbclient和mount -t cifs共享win的共享文件夹? autocad小记
插入U盘没有反应? 首先,打开设备管理器, 发现usb大容量设备为黄色感叹号 其次, 将这个usb大容量设备先卸载, 然后点击"自动扫描硬件变化",就可以重新自动安装usb的驱动. ...
- 笔记本电脑切换到无线热点无法联网问题&Spring Cloud相关工程启动报错问题
通过禁用本地网络,和禁用另一个无线网络,以及禁用后重开,修改密码,重连的方式均失败后, 使用IE浏览器浏览提示失败,点击诊断,诊断出DNS服务器无响应异常. 突然想到通过ipconfig查看ip,网关 ...
- Download and Installation of Kibana
下载以及安装 https://www.elastic.co/downloads/kibana 1.Download and unzip Kibana 2. Open config/kibana.yml ...
- switch反汇编(C语言)
在分支较多的时候,switch的效率比if高,在反汇编中我们即可看到效率高的原因 0x01分支结构不超过3个 #include <stdio.h> void main() { int x ...