this的简单了解与指向
在面试中或者在学习中我们都会接触到,this,可是这到底是什么意思? 有什么作用? 下面做一些简单的介绍。
this的误区
误区一:this指向自身
this并不是我们想的那样指向函数的本身,如下列子:
function foo(num) {
console.log("num", num);
this.count++;
}
foo.count = 0;
for (let i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log("foo", foo.count); // 这里输出foo.count为0
按照想法是,this指向的是foo函数,在for循环中,if执行了四次,按理来说foo.count输出因该为4,这样想是错误的,我们先解释怎么让代码正常输出,如下列:
function foo(num) {
console.log("num", num);
data.count++;
}
var data = {
count: 0,
};
for (let i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log("foo", data.count);
或者:
function foo(num) {
console.log("num", num);
foo.count++;
}
foo.count = 0;
for (let i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log("foo", foo.count);
这其实是用词法作用域去解决问题
误区二: this的作用域
我们通常会把this的作用域看为指向函数的作用域,这在某种情况下是错误的,this在任何情况下都不能指向函数的词法作用域,如下列:
<script>
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log("输出", this.a);
}
foo;
</script>
这里的输出是undefind
这里对常见的误区做出简单的分析,值得注意的是this不是在编译的时候被绑定的,而是在运行的时候被绑定的,this的绑定和函数的声明的位置没有任何的关系,只取决于函数的调用方式,简单地说就是this在函数调用的时候发生,它的指向取决于函数在哪里被调用。
this的绑定规则:
1、默认绑定
默认绑定这里只说非严格模式下的列子,在这里this的绑定指向的是windos(全局变量),在之前的笔记中,var声明的变量会是全局变量,结合起来如下列:
<script>
function foo() {
let a = 2;
console.log("输出", this.a);
}
var a = 10;
foo();
</script> // 输出的是10
至于为什么不是2,上面介绍了,this的指向不是指向函数自身,是10的原因当然就是规则中的默认绑定
严格模式下的默认绑定指向全局变量,非严格模式下指向undefinde
2、隐式绑定
默认绑定绑定的是全局变量往windos哪里靠,而隐式绑定我理解的是看作用域和执行上下文,下面用列子理解下:
<script>
var tom = {
age: 16,
speak: foo,
};
function foo() {
console.log("年龄", this.age);
}
tom.speak();
</script>
输出 年龄,16
这里我们思考下为什么foo()函数中的this不指向windos,回忆下我们说过,this指向不指向所在的函数内,简单的说就是谁调用,this就指向谁!
再举出一个列子:
<script>
function foo() {
console.log("年龄", this.age);
}
var jack = {
age: 34,
speak: foo,
};
var tom = {
age: 12,
jack: jack,
};
tom.jack.speak();
</script>
这里更加清楚的描述了this的指向问题,即谁调用指向谁,值得注意的是,jack必须定义在tom之上,那如果tom定义在jack之上会发生上面?为什么?
简单想想,是不是或报错? 说jack这个值undefined对吧,原因如下:
1、为undefined是因为var具有变量提升
2、变量提升后,赋值的位置不变,即Jack是在tom后赋值的,所以自然tom先调用的话这里就卡死了
上面两种方法都是隐式绑定,但是隐式绑定会出现this丢失问题,举个列子:
<script>
function foo() {
console.log(this.age);
}
var jack = {
age: 34,
speak: foo,
};
var ming = jack.speak;
var age = 100;
ming();
</script>
这是输出的是100,原因如下
var ming =jack.speak
这里ming实际上是对foo的调用,ming没有携带任何的修饰符,所以这里应用了默认绑定。
直观可能不正确的理解就是:
var ming是定义了一个全局作用域,jack.speak中的this指向指向了ming,所以指向了windos。
还有用新建的函数调用、函数的回调函数调用都会使得this丢失
新建函数调用:
<script>
function foo() {
console.log(this.age);
}
function dooFn(fn) {
fn();
}
var jack = {
age: 34,
speak: foo,
};
var age = 100;
dooFn(jack.speak); //输出100
</script>
回调函数调用:
<script>
function foo() {
console.log(this.age);
}
var jack = {
age: 34,
speak: foo,
};
var age = 100;
setTimeout(jack.speak, 1000); //输出100
</script>
3、 显示绑定
说到this显示绑定可能没什么映像,但是apply、call、bind肯定是记忆犹新,毕竟这是前端新手刚入门的噩梦,至少我这个菜鸡是的。
是的,apply、call、bind(es5提出的),能强制改变函数中的this指向,就是显示绑定了,不再准寻谁调用,this就指向谁了。
举个例子:
<script>
function foo() {
console.log(this.age);
}
var jack = {
age: 34,
speak: foo,
};
var age = 100;
setTimeout(jack.speak.apply(jack), 1000); 输出34
</script>
上面用call也是一样的,可以看出,就这样就强制把指向windos的this改为指向jack中了,是不是简单粗暴
显示绑定不能还是不能解决绑定丢失的问题,但是显示绑定中的硬绑定可以
1、硬绑定
下段代码简单了解下硬绑定:
<script>
function foo() {
console.log(this.age);
}
var jack = {
age: 34,
speak: foo,
};
// 这段是硬绑定
var bar = function () {
foo.call(jack);
};
bar();
</script>
这里可以看出foo的this改变是在bar函数内执行的,只要bar函数被调用,foo的this指向就会改变一次,这种就是简单的硬绑定,注意,bar硬绑定后,不允许再去修改绑定的this值
<script>
function foo() {
console.log(this.age);
}
var jack = {
age: 34,
speak: foo,
};
// 这段是硬绑定
var bar = function () {
foo.call(jack);
};
bar();
bar.call(window);
setTimeout(bar, 1000);
</script>
上面的输出都是34,可以看出硬绑定后this不能被修改了
硬绑定的经典场景:
1、负责接受参数并返回值
var bar = function () {
foo.call(obj,arguments);
};
2、 创建辅助函数并重复使用
关于显示绑定中的apply、call、bind我会单独记录一篇博客,这里不介绍了
4、new绑定
说起new绑定,肯定2第一时间注意new这个关键字,那么面试题中高发点就是,new的过程中发生了什么? 只去枯燥的背肯定记忆不深刻,这里从this的指向中简单的了解下
首先,用new关键字,那是使用了构造函数,但是在js中定义不一样,在js中构造函数只是一些使用new操作符时被调用的函数,它们并不会属于某个类,也不会实列化一个类。简单说就是被new操作符调用的普通函数而已,所以不存在构造函数而是对于这个函数的构造调用
下面是new过程发生的事:
1、创建(构造)一个全新的对象
2、这个new对象会被执行[[Prototype]]连接
3、这个new对象会绑定到函数调用的this
4、如果这个函数没有返回其他对象,则new表达式中的函数调用会自动返回这个对象
上面一些内容就是new绑定
this绑定的优先级
这里不上代码,直接上结论
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
this绑定列外
当让不是所有的列子都适用于这四个方法,下面举出一些列子:
1、把null、undefined作为this对象绑定在call、apply,在调用时就会忽略掉,实际上是对默认绑定的规则
<script>
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null);
</script>
输出的是2
当让直接给null可能不太好,我们可以使用这样:
var b = Object.create(null)
2、间接使用的时候
理解为就是赋值的时候
this的总结
1、this的指向和所在的函数无关、和所在的函数作用域无关,谁调用了this,this就指向谁。
2、this中new调用,this指向绑定到新创建的对象。
3、由call、apply、bind绑定this,this的指向绑定到指定的对象中。
4、默认模式下(非严格模式)this指向全局windos中,严格指向undefined.
5、在进行硬绑定后,不能再去改变同一个绑定的this指向了。
6、在箭头函数中,不适用于上述的四条规则,它是根据当前的词法作用域来判断this,简单的说就是箭头函数会继承外层的函数来调用this,无论this绑定的是什么。
对显示绑定的扩展
上面我们对显示绑定简单的做了一些描述,它与call、apply、bind这三个字段有关,下面我们简单介绍下它的用法以及区别:
apply
用法:改变函数中的this指向。
xxx.apply(对象名,数组) 就是将xxx函数中的this指向指向对象名,数组中的元素依次与元素的参数对应
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2)
var obj = {
name: 'tom'
}
fn.apply(obj, [3, 4])
call
用法:改变函数中的this指向问题,和apply的区别是第二个参数不为数组
xxx.call(对象名,参数1,参数2,参数3…) 就是将xxx函数中的this指向指向对象名,参数中的元素依次与元素的参数对应
<script>
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2)
var obj = {
name: 'tom'
}
fn.call(obj, 3, 4)
</script>
bind
用法:改变函数中的this指向问题,和call写法一样,不同的地方是bind返回的是一个函数,所以我们在调用的时候在进行传参
xxx.bind(对象)(参数1,参数2…)
<script>
function fn(a, b) {
console.log(this);
console.log(a + b);
}
fn(1, 2)
var obj = {
name: 'tom'
}
fn.bind(obj)(3, 4)
</script>
注意bind在ES5中已经提出了
推荐阅读
-
php导出word文档与excel电子表格的简单示例代码
-
Android开发之CheckBox的简单使用与监听功能示例
-
asp下tag的实现,简单介绍与部分代码
-
详解OkSocket与Android的简单使用
-
Android 中HttpURLConnection与HttpClient使用的简单实例
-
css盒子布局,浮动布局以及显影与简单的动画
-
Android开发之RadioGroup的简单使用与监听示例
-
Python实现简单的文件传输与MySQL备份的脚本分享
-
[20190319]shared pool latch与library cache latch的简单探究.txt
-
ubuntu中snap包的安装、更新删除与简单使用