this 驰骋沙场
1.环境。
在 window 环境下,全局 this 指向 window:
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
}
可以通过 this 访问 window 下的属性。
在 node 环境下,this 指向 global。
Object [global] {
DTRACE_NET_SERVER_CONNECTION: [Function],
DTRACE_NET_STREAM_END: [Function],
DTRACE_HTTP_SERVER_REQUEST: [Function],
DTRACE_HTTP_SERVER_RESPONSE: [Function],
DTRACE_HTTP_CLIENT_REQUEST: [Function],
DTRACE_HTTP_CLIENT_RESPONSE: [Function],
global: [Circular],
process:
process {
...
2.调用。
普通用法 this 指向调用它的实例。
1)全局函数
function test() {
console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
}
test();
2)定时器
window 环境定时器中的 this 指向 window。
setTimeout(function() {
console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
});
node 环境的 this 指向 Timeout.
3)字面量中的 this
var name = "bbb";
var obj = {
name: "aaa",
mes: function() {
console.log(this.name); // aaa
}
};
obj.mes();
function MES() {
console.log(this.name); // window: bbb, node: undefined
}
MES();
这个 this 在 mes 方法中,但是调用 mes 方法的是 obj,所以 this 指向 obj。MES 方法被 window 调用,所以指向全局。
再看一个例子:
var obj = {
name: "aaa",
obj: {
c: "ccc"
},
test: this.name,
text: function() {
return "text:" + this.name;
},
mes: function() {
console.log(this.obj.c);
}
};
obj.mes(); // ccc
console.log(obj.test); // undefined
console.log(obj.text()); // url:aaa
这里的操作是想在 test 属性中访问 name,但是这里的 this 是指向 window,所以会查找 window 中有没有 name 属性,如果有就是 window 中的值,没有就是 undefined。这时候可以使用 obj 调用 text()方法一个,text 方法中返回值作为值,此时的 this 指向 obj。或者 test: this.obj.name。
4)构造函数
指向构造函数的实例
function Test(name, age) {
this.name = name;
this.age = age;
}
var test = new Test("ooo");
console.log(test.name); // ooo
5)事件绑定
指向绑定事件的 DOM
var dom = document.getElementById("div-id");
dom.onclick = function() {
console.log(this); // DOM
};
6)原型
function Test(name, age) {
this.name = name;
this.age = age;
}
Test.prototype.mes = function() {
console.log(this.name); // 指向构造函数 Test { name: 'ooo', age: undefined }
};
var test = new Test("ooo");
test.mes();
9)改变指向
使用 call、apply、bind
var obj = {
name: "aaa",
age: "bbb"
};
function mes(info) {
console.log(this.name + info);
}
console.log(mes()); // window/golbal
mes.call(obj, "呵呵"); // 'aaa呵呵'
// mes.apply(obj, ["呵呵"]);
// mes.bind(obj, "呵呵")();
在这里 call 和 appl 的使用方法相同,不同的是 call、apply 传的参数。call(this,arg,arg),
apply(this,arguments)。call 第二个参数是单个添加的序列,apply 第二个参数为参数数组。
bind 和 call、apply 的区别是,call/apply 直接作用在函数上并执行函数,而 bind 作用在 mes 函数上返回一个函数,所以不会立即执行。
bind 方法的实现:
Function.prototype.bind = function(context) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);
var bound = function() {
if (this instanceof bound) {
// 如果是作为构造函数,那么第一个参数 context 就不需要了
return that.apply(
this,
args.concat(Array.prototype.slice.call(arguments))
);
} else {
// 拼接参数
return that.apply(
context,
args.concat(Array.prototype.slice.call(arguments))
);
}
};
// 维护原型关系
if (this.prototype) {
bound.prototype = this.prototype;
}
return bound;
};
这里频繁使用了
Array.prototype.slice.call()
,slice 是 Array 类里的一个方法,Array.prototype.slice
表示截取数组的一部分,slice(start,end): 返回 start 到 end (不包括该元素)的 arrayObject 中的元素,如果没有 end 参数,则从选 start 至末尾的元素。call(arguments,1)表示从第二个参数开始。
function test(a, b, c, d) {
var arg = Array.prototype.slice.call(arguments, 1); // 调用slice(1)
alert(arg);
}
test("a", "b", "c", "d"); // [ 'b', 'c', 'd' ]
Array.prototype.slice.call()可以理解为:改变数组的 slice 方法的作用域(因为 arguments 并不是真正的数组对象,只是与数组类似,它并没有 slice 这个方法),在特定作用域中去调用 slice 方法,call()方法的第二个参数表示传递给 slice 的参数即截取数组的起始位置。
原理: Array.prototype.slice.call(arguments)能将具有 length 属性的对象(key 值为数字)转成数组。[]是 Array 的示例,所以可以直接使用[].slice()方法。
var obj = { 0: "hello", 1: "world", length: 2 };
console.log(Array.prototype.slice.call(obj, 0)); //["hello", "world"] 调用slice(0)
没有 length 属性的对象
var obj = { 0: "hello", 1: "world" }; //没有length属性
console.log(Array.prototype.slice.call(obj, 0)); //[]
10)es6
默认指向在定义它时,它所处的对象,而不是执行时的对象, 定义它的时候,可能环境是 window(即继承父级的 this)
var obj = {
say: function() {
setTimeout(() => {
console.log(this);
});
}
};
obj.say(); // obj
var obj = {
say: function() {
var f1 = () => {
console.log(this); // obj
setTimeout(() => {
console.log(this); // obj
});
};
f1();
}
};
obj.say();
f1 继承父级 this 指代的 obj,不管 f1 有多层箭头函数嵌套,都是 obj.
var obj = {
say: function() {
var f1 = function() {
console.log(this); // window, f1调用时,没有宿主对象,默认是window
setTimeout(() => {
console.log(this); // window,继承父级的this,父级的this指代的是window.
});
};
f1();
}
};
obj.say();