java里this作用域,Javascirpt学习笔记-This

c2052e39ad5d

Javascript中this.png

1. 关于This

相比在Java中this只指代当前类的用法,Javascript中this的并不是一个固定的指代,其指代存在绑定规则

1.1 使用原因

其实大部分情况下,使用词法作用域已经可以解决所有需要使用this的场景,代码中完全可以不使用this。但是使用this可以带来函数调用过程中对于函数参数传递的优化。举个例子:

function foo(a, b){

console.log(a + b);

}

f(1, 2); // 3

在函数调用过程中需要传递两个参数进行调用,但是如果参数是某个对象中的某些属性,那么使用这种调用方式就变成如下情况:

var obj = {a: 1 ,b: 2};

foo(obj.a, obj.b); // 3

在传递参数的时候明显比较麻烦,尤其在于如果要利用对象中的复数属性的时候,参数传递就变得特别多,这个时候使用this就可以进行简化:

function foo() {

console.log(this.a + this.b);

}

var obj = {a: 1, b: 2};

foo.apply(obj); // 3

当然我们完全可以将obj作为参数进行传递,所以说使用this并非开发中必须。

1.2 绑定要点

this到底代表什么关键在于this的绑定规则,但无论什么规则,首先要明确两点:第一,this不代表自身,第二,this和词法作用域没有必然联系例:

function foo() {

console.log(this.a); // 这里的this并不指代foo函数本身,也和词法作用域无关

}

以上两点从根本上来说指在函数进行声明的时候,并不能确定this代表什么,只有在函数调用的时候,this的绑定关系才可以确定(就像动态作用域一样)

this只有在函数调用的时候确定绑定关系

this只有在函数调用的时候确定绑定关系

this只有在函数调用的时候确定绑定关系

重要的事情说三遍,不过再补充一下:

箭头函数是特别的

箭头函数是特别的

箭头函数是特别的

2. 绑定规则

根据Javascript中的用法,this有以下的几种绑定规则:

2.1 全局绑定

直接在Javascript全局中使用this,这个时候this指向一个全局对象,在浏览器中代表window对象,node中代表global对象:

console.log(this);

2.2 Function Invoke(默认绑定)

直接调用方法的时候,如果在严格模式下,this的绑定为undefined(之所以返回undefined,是因为函数调用的时候执行的上下文并不确定,严格模式于是将执行中的this绑定为undefined),非严格模式下,this绑定为全局对象,同 2.1 全局绑定 :

// 1. 非严格模式下

function foo(){

console.log(this);

}

foo(); // window

// 2. 严格模式下

(function(){

'use strict'

function foo() {

console.log(this);

}

foo(); // undefined

})()

// 3. 严格模式下调用

(function(){

'use strict'

foo(); // window

})()

请注意例子中的第三个调用,在调用的时候使用了严格模式,但是打印结果并不是undefined,因此说明严格模式是在函数声明的时候对this的绑定产生影响,在调用的时候对此并没有影响。

如果之后所有的规则都不符合的时候,将使用这一条规则,来进行this的绑定。

2.3 Method Invoke(隐式绑定)

当使用对象调用的时候,this将绑定为该对象:

var obj = {

a: 1,

foo: function() {

console.log(this.a);

}

}

obj.foo(); // 1

但是隐式绑定过程存在两种隐式丢失的情况(可以使用箭头函数解决,详见 2.6 箭头函数),使用函数别名和回调函数:

var obj = {

a: 1,

foo: function() {

console.log(this.a);

}

}

// 1. 使用函数别名

var baz = obj.foo;

baz(); // undefined

// 2. 使用回调函数

setTimeout(obj.foo , 0); // undefined

原因在于在使用函数别名和回调函数的时候,可以理解为进行了以下的操作

var baz = obj.foo = function() {

console.log(this.a);

}

于是,在调用baz的时候,将根据 2.2 默认绑定 对this进行处理

2.4 apply, call, bind (显示绑定)

使用显示绑定的时候,this绑定为传入的对象:

function foo(a, b, c){

console.log(this.a + a + b + c);

}

foo.apply({a: 1}, [1, 2, 3]) // 1 + 1 + 2 + 3 = 7

foo.call({a: 1}, 1, 2, 3) // 7

foo.bind({a: 1})(1, 2, 3) // 7

apply和call的使用本质上没有区别,只是传递参数不同

bind是ES5之后Function原型链上增加的方法,先将this绑定到传递的对象,但是使用bind进行绑定的时候是硬绑定,硬绑定是指这个绑定只能绑定一次,只有第一次绑定可以生效:

function foo() {

console.log(this.a);

}

foo.bind({a: 1}).bind({a: 2})(); // 1

因为现在很多内置对象提供了原型链上的方法,于是通过这种原型链上API的调用是显示调用的过程

function foo(i) {

console.log(this.a + i);

}

[1,2,3].forEach(foo, {a: 1}); // 2 3 4

2.5 new(构造调用)

使用new进行绑定的时候,this绑定为构建函数构造的新对象,例:

function F() {

this.a = 1;

}

var f = new F(); // 等同于 var f = new F;

console.log(f.a); // 1

这里使用new进行构造的时候,Javascript会按照以下的过程执行

创建一个新对象 --> 将对象和构造方法的prototype做关联 --> 将this绑定到该对象 --> 如果构造函数无返回值则返回该对象,否则返回新对象

所以,转换为代码理解也就是:

// 1. 无返回的构造函数

function F() {

this.a = 1;

}

/* 使用new以后,函数内部变化为

function F() {

var _o_ = {} ; // 创建一个新对象

_o_.prototpye = F.prototype; // 原型链关联

_o_.a = 1; // this绑定到对象

return _o_; // 返回该对象

}

*/

var f = new F();

console.log(f.a); // 1

// 2. 有返回的构造函数

function F() {

this.a = 1;

return {b: 2};

}

/* 使用new以后,函数内部变化为

function F() {

var _o_ = {} ; // 创建一个新对象

_o_.prototpye = F.prototype; // 原型链关联

_o_.a = 1; // this绑定到对象

return {b: 2}; // 返回原返回值

}

*/

var f = new F();

console.log(f.a) // undefined

console.log(f.b) // 2

于是,如果原构造函数存在返回值的情况下,this绑定为了函数内部创建的新对象,但是这个新创建的对象并没有返回,所以没办法再对这个绑定的内容进行引用了。

2.6 箭头函数

使用箭头函数进行绑定的时候,this绑定外层(函数或者全局)作用域环境:

var obj = {

a: 1,

foo: () => {

console.log(this);

}

}

obj.foo(); // window

这个例子看上去和 2.3 隐式绑定 是差不多的,但是区别这里的this指向发生变化,this不再指向调用对象本身,而是直接使用外层函数全局对象。

但是下面的例子是需要注意的:

function f(){

return function() {

console.log(this.a);

}

}

f.apply({a:1}).apply({a:2}) // 2;

// 箭头函数

function f(){

return() => {

console.log(this.a);

}

}

f.apply({a:1}).apply({a:2}) // 1

在第一次调用 apply的时候,生成了一个外层的作用域,此时this指向了该外层全局对象的{a: 1},之后不会再发生变化

2.7 事件绑定

使用事件函数进行绑定的时候,根据使用情况不同,this通常指向当前事件节点,例:

// 1. 使用addEventListener等事件绑定,this指向绑定的节点

click

document.getElementById('d').addEventListener('click', function(e){

console.log(this === e.currentTarget); // true

console.log(this === e.target); // true

})

// 2. 使用html事件绑定,this指向当前dom节点

click

// 3. 使用html事件绑定,增加IIFE,this指向window

click

3. 优先级

当我们如果使用的情况比较复杂,同时存在以上几种绑定关系的时候,我们该如何处理?实际上绑定的关系存在一个优先级,按照优先级来进行处理(这里除开 2.7 事件绑定,因为通常事件绑定下不会存在那么复杂的关系)

2.5 构造绑定 > 2.4 显示绑定 > 2.3 隐式绑定 > 2.2 默认绑定

2.6 箭头函数 有特殊表现

例:

// 1. 隐式绑定 > 默认绑定

function foo() {

console.log(this.a);

}

var obj = {a: 1, foo: foo};

obj.foo(); // 1

// 2. 显示绑定 > 隐式绑定

obj.foo.apply({a: 2}) // 2

// 3. 构造绑定 > 显示绑定

function F(a) {

this.a = a;

}

var obj = {};

var bar = F.bind(obj);

bar(1);

console.log(obj.a); // 1

var obj2 = new bar(2);// 使用new修改了this的绑定

console.log(obj.a); // 1 原来的值没有发生变化

console.log(obj2.a); // 2 新的值发生了变化

// 4. 箭头函数的特殊表现

var a = 2;

var f = () => {

console.log(this.a);

}

f.apply({a: 3}); // 2

4. 其他

4.1 显示绑定中null的使用

在进行显示绑定的时候,如果传递绑定对象为null时,因为不符合我们说的其他规则,所以将使用默认规则:

function foo() {

console.log(this);

}

foo.apply(null) // window

但是如果直接传递null进行方法调用存在变量泄漏的风险:

function foo() {

this.a = 1;

}

foo.apply(null);

console.log(a); // 1

4.2 Object.create(null)

为了解决 4.1 显示绑定中null的使用 存在的变量泄漏风险,于是可以使用Object.create(null)创建一个空对象,使用这种方式创建的空对象,比{}更纯粹,因为他不继承Object.prototype

function foo() {

this.a = 1;

}

foo.apply(Object.create(null));

console.log(a); // ReferenceError

5. 参考

6. 练习

惯例最后来个练习吧

function foo() {

console.log(this.a);

}

var obj = {

a: 1,

foo: foo

}

var obj2 = {

a: 1,

foo: () => {

console.log(this.a);

}

}

function F() {

this.a = 2;

}

var f = new F();

foo(); // undefined

obj.foo(); // 1

obj.foo.bind({a: 2})(); // 2

obj2.foo.bind({a: 2})(); // undefined

console.log(f.a); // 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值