this
目录
一、上几道题开开胃
1 🤔
var obj = {
name: 'a',
say: function(){
console.log(this.name)
}
}
var say = obj.say;
obj.say();
say();
答案
a
undefined
在对象里面调用 和 被声明成独立的全局引用 是不一样的
obj.say();
就是在 obj
对象里面查找 say
函数,执行环境是 obj
对象,this
指向 obj
,this.name
就是 obj
里面的 name
var say = obj.say;
这时候 say
被声明成全局函数,在全局作用域下没有 name
这个变量,输出 undefined
;如果在全局下给 name
赋值一个值,say()
就指向那个值了
注:这类题对应下文中【二、理解两种函数调用的写法差异】,是阮一峰教程中的经典例子
2 🤨
var a = 1;
function foo(){
console.log(this.a)
}
var obj = {
a: 2,
foo: foo
};
obj.foo();
答案
2
foo
是在 obj
里被调用的,foo
的地址被指向 obj
的 "foo"
属性,即 foo
函数的环境就是 obj
对象
3 😤
var length = 10;
function fn () {
console.log(this.length);
}
var obj = {
length: 5,
method: function (fn) {
fn();
}
};
obj.method(fn);
答案
10
在外面的全局变量 length
是 10
,在 obj
对象里面的 length
是 5
这里 fn
是被当做 method
的参数传入的,但是调用它的不是 obj
对象,而是 window
,所以输出全局变量 10
如果把 obj
改造成加上 call 的绑定 this
,输出的结果就不一样了
var obj = {
length: 5,
method: function (fn) {
// 在这里用call 将 this 指向 obj 自己
fn.call(this);
}
};
输出对象内部的 5
二、理解两种函数调用的写法差异
from 阮一峰的网络日志
学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果。
var obj = {
foo: function () {}
};
var foo = obj.foo;
// 写法一
obj.foo()
// 写法二
foo()
这两种写法的结果是不一样的
对于obj.foo()
来说,foo
运行在obj
环境
对于foo()
来说,foo
运行在全局环境
三、this 是什么
this
是 JavaScript 中的一个关键字
被运用于函数体内,依赖于函数调用的上下文条件,与函数被调用的方式有关
this
的指向,完全是由函数被调用的调用者来决定的
this
是在运行时绑定,而与编写时的绑定无关
this
可以说是一种简洁函数表达,优化值的传递的一种方法,不可能每次都给函数具体的传值参数,会有很多隐式的引用形式的数据,用 this
表达更简洁
🧁 this 指向的就是函数运的执行环境
JavaScript 允许在函数体内部,引用当前环境的其他变量。
所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。
所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
稍微地总结一下:
- 独立函数调用时,
this
指向全局对象(window
);如果使用严格模式,this
绑定至undefined
- 函数
this
是指向调用者
四、几种不同函数不同的 this 指向
一共6种函数
1️⃣普通函数
//定义
function fn(){
console.log('hello js');
}
//调用
fn();
fn.call();
this
指向 window
2️⃣对象的方法
//定义
var ob = {
say: function(){
console.log('hello js');
}
}
//调用
ob.say();
this
指向对象
3️⃣构造函数
//定义
function Run(){};
//调用
var a = new Run();
this
指向(a
)实例对象
4️⃣绑定事件函数
//定义
btn.onclick = function(){};
//调用
点击就可以调用这个函数
this
指向函数的调用者(btn对象)
5️⃣定时器函数
//定义
setInterval(function(){}, 1000);
//调用
函数是定时器自动1秒钟调用一次
this
指向 window
6️⃣立即执行函数
//定义
(function(){
console.log('hello js');
})();
//调用
立即执行函数是自动调用
this
指向 window
五、箭头函数中的 this
箭头函数使得 this
从“动态”
变成“静态”
(固定)
箭头函数体内的 this
对象,就是 定义 时所在的对象,而不是 使用 时所在的对象。
普通函数的 this
,是运行时的对象。
一个例子
var id = 10;
// 普通函数
function foo() {
setTimeout(function() {
console.log(this.id);
}, 100);
}
// 箭头函数
function foo1() {
setTimeout(() => {
console.log(this.id);
}, 100);
}
foo(); // 10
foo1(); // 10
当没有其他条件值的参与的时候,调用两个函数。
普通函数的 this
在运行时绑定,定时器的 this
指向 window
,即全局变量 id
= 10
箭头函数的 this
在定义时绑定,定义时在函数作用域内找不到 id
,跳到外层找到全局变量 id
= 10
现在我们加上 call 绑定
var id = 10;
// 普通函数
function foo() {
setTimeout(function() {
console.log(this.id);
}, 100);
}
// 箭头函数
function foo1() {
setTimeout(() => {
console.log(this.id);
}, 100);
}
var obj = {
id: 100
}
foo.call(obj); // 10
foo1.call(obj); // 100
把两个函数绑定给了 obj
对象
对于箭头函数,让 this
绑定了定义时所在的作用域 – obj
,所以输出的 id
是 obj
对象里的 100
普通函数,运行时定时器还是在全局作用域上,输出的还是全局变量 id
= 10
六、apply call bind 的用法
apply
调用一个具有给定 this
值的函数,改变函数的 this
指向
以数组(伪数组)的形式传递
function.apply(thisArg, [argsArray])
-
thisArg:在
func
函数运行时使用的this
值 -
argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
func
函数
具体写
var f = function() {
console.log(this.x)
}
var x = 1;
f(); // 1 全局变量 x 的1
var b = {
x: 2,
y: 4,
z: 8
}
f.apply(b, [x]); // 2
将 this
绑到第一个参数上,一般以对象的形式传输
把参数用得更透彻一些
var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
// array: ["a", "b", 0, 1, 2]
求数组最大值 最小值
Math.max
不能对数组进行操作,只能加上…
var arr = [1, 3, 13, 34, 6];
var max = Math.max(...arr);
console.log(max) // 34
使用 apply
var arr = [1,3,13,34,6];
var max = Math.max.apply(Math, arr);
var min = Math.min.apply(Math, arr);
console.log(max, min);
call
调用函数,改变函数内的 this
指向
call 的主要作用可以实现继承
接受的是参数列表
function.call(thisArg, arg1, arg2, ...)
- thisArg:在 function 函数运行时使用的 this 值。
- arg1, arg2, …:指定的参数列表。
一个简单的例子,打印名字
var name = "none";
function identify() {
return this.name.toUpperCase();
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify(); // "NONE"
identify.call(me); // "KYLE"
identify.call(you); // "READER"
bind
不会调用函数,改变函数内部 this
指向,且会产生一个新的函数
function.bind(thisArg, arg1, arg2...)
-
thisArg:在 function 函数运行时指定的
this
值 -
arg1, arg2:传递的其他参数
具体写
关于定时器中的 this
指向
var name = 1;
var myObj = {
name: 2,
showName: function () {
console.log(this.name);
}
}
setTimeout(myObj.showName, 1000) // 输出1
定时器上下文中的 this
会被设置为全局 window
(如果是严格模式,会被设置为 undefined
)
所以输出的 this.name
是全局的 name
,与 myObj
对象无关了
通过 bind 改变 this
绑定,绑定到 myObj
对象上
setTimeout(myObj.showName.bind(myObj), 1000)
总结
相同点:
- 都可以改变函数内部的 this 指向
不同点:
- call 和 apply 会调用函数,并且改变函数内部的 this 指向
- call 和 apply 传递的参数不一样,call 传递单个参数,apply 传递数组形式
- bind 不会调用函数,可以改变函数内部的 this 指向
- bind 会产生一个新的函数
主要应用场景:
- call 经常做继承
- apply 跟数组有关系,借助于数学对象
- bind 用于定时器内部指向