读起来使你有新认识或可以使你离更确切的定义更近时的文章不应该被忽略。
this
this既不指向函数自身,也不指向函数的词法作用域(ES6中箭头函数采用词法作用域)。
this实际上是函数被调用时才发生绑定。
this指向什么取决于如何调用函数,谁调用的this,this就指向谁。

1、默认绑定
当独立函数被调用时,不管是否在调用栈中,this都指向全局对象(浏览器中为window)。
默认绑定不适用于严格模式。

<script>
var a = 2;
function foo() {
console.log(a); // 也可以this.a,这里this只window,this.a = 2;
}
function bar() {
var a = 5;
foo();
}
bar(); // 2
</script>

2.1、隐式绑定

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定这个上下文对象。
对象属性引用链中只有最后一层在调用位置中起作用。
要求:对象内部必须包含一个指向函数的属性,该对象可通过这个属性间接引用函数。

<script>
function foo() {
console.log(this.a)
} var obj2 = {
a: 42,
foo: foo
}; var obj1 = {
a: 2,
obj2: obj2
}; obj1.obj2.foo(); // 42
</script>
2.2、隐式丢失
<script>
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 这里bar将引用foo函数本身,所以不带有函数对象上下文。
var a = "oops, global"; // a 是全局对象属性
bar(); // "oops, global"
</script>
2.3、回调函数的情况下(参数传递时的隐式赋值)
<script>
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// 参数传递时fn = obj.foo,fn引用foo函数本身,同样不带有函数对象上下文
fn();
} var obj = {
a: 2,
foo: foo
}; debugger
var a = "oops, global";
doFoo(obj.foo); // "oops, global"
</script>

3、显示绑定

采用call()和apply(),通过传入一个对象(若为基本类型,会被封装函数转为对象——装箱),将this绑定到该对象。

基本类型(String|Boolean|undefined|null|Number)

引用类型(Object,里面包括Function|Array|Date)

<script>
function foo() {
console.log(this.a);
} var obj = {
a: 2
}; // 硬绑定后bar,无论怎么调用,都不会影响foo函数的this绑定。
var bar = function () {
foo.call(obj);
}; var a = "oops, global";
bar(); // 2
setTimeout(bar, 100); // 2
bar.call(window); // 2 // var obj = {a: 2}换成 var obj = "str"时,函数foo中的this为String,打印出来三个undefined(String对象中没有属性)
</script>

硬绑定典型应用——包裹函数

<script>
function foo(something) {
console.log(this.a, something);
return this.a + something;
} var obj = {
a: 2
}; var bar = function () {
return foo.apply(obj, arguments); // 将obj对象硬编码进去
}; var b = bar(3); // 2, 3 注:arguments可以是bar函数传进来的参数,也可以直接某一具体数组,如[5],这时打印出2, 5
console.log(b); // 5 如果arguments为[5],则输出7
</script>
<script>
function foo(something) {
console.log(this.a, something);
return this.a + something;
} function bind(fn, obj) {
return function () {
return fn.apply(obj, arguments); // 利用参数将obj传入
};
} var obj = {
a: 2
}; debugger;
var bar = bind(foo, obj); // bind(foo, obj)会返回一个包裹函数
var b = bar(3); // 2 3
console.log(b); // 5
</script>

上述包裹函数,想要包裹其他函数,只能一个一个重复写,硬编码的方式导致不能被重用,当某种功能需要多次重复使用时,将其抽象出来,成为函数。

4、new 绑定

任何函数都可能被用作构造函数,当函数被new操作符“构造调用”时,会执行以下操作:

1、创建一个新对象(若该函数不是JS内置的,则创建一个新的Object对象);

2、将this绑定到这个对象;

3、执行构造函数中的代码(为这个新对象添加属性);

4、若函数没有返回其他对象,则自动返回这个新对象;若函数有return返回的是非对象,则还是自动返回这个新对象,即覆盖那个非对象。

<script>
function foo(a) {
this.a = a;
}
var bar = new foo(2); // bar 即为foo 这里的foo指的是一个新创建的对象,this也指向它,this.a 即是为该对象添加属性。这里我想告诉自己的是这里的foo对象并不能看做console.log(foo)的foo
console.log(foo); // ƒ foo(a) {this.a = a;} foo是个函数对象
console.log(typeof foo); // function
console.log(typeof bar); // object
console.log(foo instanceof Function); // true
console.log(bar instanceof Function); // false
console.log(bar instanceof Object); // true
console.log(bar.a); // 2
console.log(new foo(4).constructor ); // f foo(a) {this.a = a} 返回的是个函数foo啊
console.log("------------------------------------"); /*
var bar = new foo(2);
这里其实是将new foo(2)创建的对象赋值给bar,创建的新对象为foo【new foo(2)和bar的constructor都为foo】。
函数构造后,函数内的this指向指着函数名,也就是bar其实指的是foo对象,
当console.log(bar == foo)是false的, 因为这里的话bar是对象,而foo则是指function foo(a) {this.a = a;}
*/ </script>
<script>
function returnObj(c) {
this.c = c;
return {d: "Hei~"};
} var obj = new returnObj(1); // 构造函数有返回值后,this指向返回的对象【{d: "Hei~"}】,不是returnObj了,this.c是作为returnObj的属性。
console.log(bar.c); // undefined
console.log(obj instanceof Object); // true
console.log(obj.constructor); // ƒ Object() { [native code] }
console.log(new returnObj(2).c); // undefined
console.log(new returnObj(4).d); // Hei~
</script>
这部分内容记录来来自于《你不知道的JS
<script>
var fun = {
'foo': 'foo1',
'bar': 'bar1'
}; var o = Object.create(fun);
console.log(o.foo); // foo1
console.log(o.hasOwnProperty("foo")); // false
console.log(o.__proto__.foo); // foo1
</script> <script>
function NothingSpecial() {
console.log("Dont't mind me");
}
var a = new NothingSpecial(); // Dont't mind me
console.log(a); // NothingSpecial {}
</script>
<!--
NothingSpecial 只是一个普通的函数, 但是使用 new 调用时, 它就会构造一个对象并赋值
给 a, 这看起来像是 new 的一个副作用(无论如何都会构造一个对象)。 这个调用是一个构
造函数调用, 但是 NothingSpecial 本身并不是一个构造函数。
换句话说, 在 JavaScript 中对于“构造函数” 最准确的解释是, 所有带 new 的函数调用。
函数不是构造函数, 但是当且仅当使用 new 时, 函数调用会变成“构造函数调用”
-->

间接引用

<script>
function foo() {
consoles.log(this.a);
} var a = 2;
var o = {a: 3, foo: foo};
var p = {a: 4}; foo(); // 2
o.foo(); // 3
(p.foo = o.foo)(); // 2, 由于p.foo = o.foo返回的是目标函数的引用,所以调用位置是foo(),而不是p.foo()或o.foo()
</script>

1、默认绑定当独立函数被调用时,不管是否在调用栈中,this都指向全局对象(浏览器中为window)。默认绑定不适用于严格模式。

随机推荐

  1. MySQL 中有哪些不同的表格?

    共有 5 种类型的表格: 1.MyISAM 2.Heap 3.Merge 4.INNODB 5.ISAM

  2. mac-变量

    去除每次都要source : 加入:

  3. memcached 的 cache 机制是怎样的?

    Memcached 主要的 cache 机制是 LRU(最近最少用)算法+超时失效.当您存 数据到 memcached 中,可以指定该数据在缓存中可以呆多久 Which is forever, or ...

  4. 构造器注入和 setter 依赖注入,那种方式更好?

    每种方式都有它的缺点和优点.构造器注入保证所有的注入都被初始化,但是 setter 注入提供更好的灵活性来设置可选依赖.如果使用 XML 来描述依赖, Setter 注入的可读写会更强.经验法则是强制 ...

  5. ubuntu sublime text3 python 配置 sublime text3 python 配置

    ubuntu sublime text3 python 配置     1.安装sublime text 3 安装过程非常简单,在terminal中输入: sudo add-apt-repository ...

  6. 安卓性能优化之计算apk启动时间

    之前有人在知乎提问:"怎么计算apk的启动时间?" : 利用Python或者直接用adb命令怎么计算apk的启动时间呢?就是计算从点击图标到apk完全启动所花费的时间.比如,对游戏 ...

  7. 布局框架frameset

    <!DOCTYPE html>//demo.html <html> <head> <meta charset="UTF-8"> &l ...

  8. CTF大赛模拟-CFS三层内网漫游

    CTF大赛模拟-CFS三层内网漫游 环境: 三台虚拟机,三个网络. target 1:192.168.161.178 ,192.168.52.132 (linux) target 2:192.168. ...

  9. 阿里云-部署-服务-Docker

    目录 ♫ MusicPlayer Naiveboom - 比较安全 个人阿里云部署的小服务,欢迎使用,服务器资源有限,如果遇到卡顿还请谅解~ 索引: 在线音乐播放器 阅后即焚 ♫ MusicPlaye ...

  10. Alibaba Java诊断工具Arthas查看Dubbo动态代理类

    原创/朱季谦 阅读Dubbo源码过程中,会发现,Dubbo消费端在做远程调用时,默认通过 Javassist 框架为服务接口生成动态代理类,接着再去调用代理类实现远程接口调用.在阅读这部分源码时,最后 ...