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 ...
随机推荐
- python---01.各类计算机语言,python历史,变量,常量,数据类型,if条件
一.认识计算机 1.硬件组成: CPU(大脑) + 内存(缓冲) + 主板(连接各部分) + 电源(心脏) + 显示器 + 键盘 +鼠标+ 显卡 + 硬盘 2.操作系统 ①windows ...
- topcoder srm 685 div1
problem1 link 依次枚举每个元素$x$,作为$S$中开始选择的第一个元素.对于当前$S$中任意两个元素$i,j$,若$T[i][j]$不在$S$中,则将其加入$S$,然后继续扩展:若所有的 ...
- Android.bp 添加宏开关【转】
本文转载自:https://github.com/zzb2760715357/document/blob/master/android_doc/Android.bp%E6%B7%BB%E5%8A%A0 ...
- secureCRT的自动化脚本如何编写?
以等待字符串eth0的出现,出现后或者20秒后脚本执行reboot命令的脚本为例,示例如下: #$language = "VBScript" #$interface = " ...
- 【控制分片分配】控制Elasticsearch分片和副本的分配
ES集群中索引可能由多个分片构成,并且每个分片可以拥有多个副本.通过将一个单独的索引分为多个分片,我们可以处理不能在一个单一的服务器上面运行的大型索引,简单的说就是索引的大小过大,导致效率问题.不能运 ...
- Java 实现一个自己的显式锁Lock(有超时功能)
Lock接口 package concurency.chapter9; import java.util.Collection; public interface Lock { static clas ...
- 完全卸载oraclean安装
完全卸载oracle11g步骤:1. 开始->设置->控制面板->管理工具->服务 停止所有Oracle服务.2. 开始->程序->Oracle - OraHome ...
- 题解——loj6280 数列分块入门4 (分块)
分块维护一个区间和 然后记得更新的时候左边角块的tag不要打错到右边角块 #include <cstdio> #include <algorithm> #include < ...
- 深度学习课程笔记(九)VAE 相关推导和应用
深度学习课程笔记(九)VAE 相关推导和应用 2018-07-10 22:18:03 Reference: 1. TensorFlow code: https://jmetzen.github.io/ ...
- 什么是SpringCloud
SpringCloud是在SpringBoot的基础上构建的,用于简化分布式系统构建的工具集. 该工具集为微服务架构中所涉及的配置管理,服务发现,智能路由,断路器,微代理和控制总线等操作 提供了一种简 ...