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 用于定时器内部指向