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;