Javasctipt 的 this
函数的this关键字在JavaScript中的行为与其他语言稍有不同。严格模态与非严格模态也有一定的区别。在大多数情况下,这个值由函数的调用方式决定。它不能在定义期间通过赋值来确定,而且每次调用函数时它可能是不同的。ES5引入了bind()方法来设置函数this的值,不管它是如何调用的,而ES2015引入了箭头函数,它不提供自己的this绑定(它保留了所包含的词法上下文的这个值)。
一、在全局环境中的this
在浏览器环境中
直接输出this
console.log(this); // Window
console.log(this === window); // true
console.log(this === Window); // false
window instanceof Window // true
上述结果说明直接输出this时,输出的是它的构造函数,但它其实是一个实例;
‘use strict’
console.log(this); // undefined
严格模式下,全局的this指向undefined
在node环境中
直接输出this
console.log(this); // {}
严格和非严格模式都是 {}
二、函数中的this
直接在全局环境中调用,而不是作为某个对象的属性或方法
function fn() {
return this;
}
let obj = {
a: 1,
b: fn
}
fn() // node: global , browser: window
let objFn = obj.b;
objFn(); // node: global , browser: window
// 箭头函数
let obj2 = {
a: 2,
b: () => this
}
let obj2Fn = obj2.b;
obj2Fn(); // node: global , browser: window
// 立即执行函数
!function(){
console.log(this === window) // true
}()
let obj = {
say: function() {
console.log(this === window) // true
}()
}
obj.say;
let str = ‘windows’
let o = {
str: ‘o’,
methods: {
str: ‘methods’,
fn: function () { console.log(this.str) },
arrowFn: function() { // IIFE
let fn = () => { console.log(this.str) }
return fn;
}()
}
}
o.methods.arrowFn(); // undefined ;此时,this指window;而用let声明的str变量不会添加到window对象上去,所以为undefined;
在非严格模式下
function fn() {
‘use strict’
return this;
}
fn() // undefined
作为对象的属性调用
function fn() {
return this;
}
let obj1 = {
a: 1,
b: fn
}
let obj2 = {
a: 2
}
obj2.b = obj1.b;
obj1.b(); // {a: 1, b: ƒn}
obj2.b(); // {a: 2, b: ƒn}
// 箭头函数
let obj1 = {
a: 1,
b: () => this
}
let obj2 = {
a: 2
}
obj2.b = obj1.b;
obj1.b(); // node: global , browser: window
obj2.b(); // node: global , browser: window
通过call 和 apply 方法调用
如果函数在其主体中使用this关键字,则可以使用call()或apply()方法将其值绑定到调用的特定对象
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {a: 1, b: 3};
add.call(o, 5, 7); // 16
add.apply(o, [10, 20]); // 34
//
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call(‘foo’); // [object String]
通过 bind 方法来绑定this
function fn() {
return this.a;
}
let k = fn.bind(null);
k(); // undefined
//此时this === window
let g = fn.bind({a: 1});
g(); // 1
let m = fn.bind({a: 2});
m(); // 2
let h = g.bind({a: 3}); // bind只会绑定一次
h(); // 1
// 正常返回值
let obj = {
a: 1,
say: (function() {
let _say = function() {
console.log(this.a);
}
return _say;
})()
}
obj.say(); // 1
// bind obj
var obj = {
say: (function() {
let _say = function() {
console.log(this === window);
}
return _say.bind(obj);
})()
}
obj.say(); // true
// bind的简单实现
Function.prototype.myBind = function(context){
self = this; //保存this,即调用bind方法的目标函数
return function(){
return self.apply(context,arguments);
};
};
let g1 = fn.myBind({a: 1})
g1(); // 1
let h1 = g1.myBind({a: 3})
h1(); // 报错,
原理
this指的是函数运行时所在的环境,执行上下文
JavaScript 将对象存储在内存中,会将存储的地址赋给我们所申明的变量;
var o = {a: 5};
//内存中会这样存储 o => a => {
// foo: {
// [[value]]: 5
// [[writable]]: true
// [[enumerable]]: true
// [[configurable]]: true
// }
//}
var f = function () {}
var m = {a: f}
// 当a的值是一个函数的时候,就会变成以下形式:
//内存中会这样存储 o => a => {
// foo: {
// [[value]]: f的地址
// [[writable]]: true
// [[enumerable]]: true
// [[configurable]]: true
// }
//}
this的作用
由上面可以看出,由于函数单独存储,可以单独运行,运行在不同的上下文中。但要有一个机制来表示它,this就是这个作用,能够指向当前函数的运行环境;
箭头函数原理的思考
箭头函数又和上文说的内容不相符。其实仔细想想,它是个语法糖,等同于给函数运用了bind方法,绑定函数的父作用域执行环境
let str = ‘windows’
let o = {
str: ‘o’,
methods: {
str: ‘methods’,
fn: function () { console.log(this.str) },
arrowFn: () => { console.log(this.str) }
}
}
let str = ‘windows’
let o = {
str: ‘o’,
methods: {
str: ‘methods’,
fn: function () { console.log(this.str) },
arrowFn: function() {
let fn = () => { console.log(this.str) }
return fn;
}
}
}
o.methods.fn(); // methods
o.methods.arrowFn()(); // methods;
发表回复