JavaScript 中 this 老是指向一个对象编程
this 指向该对象,如:设计模式
var obj = {
a: 1,
getA: function() {
console.log(this === obj); // true
console.log(this.a); // 1
}
};
obj.getA();
复制代码
当函数不做为对象的属性被调用时,普通函数的 this 老是指向全局对象,浏览器里就是 window 对象。数组
var name = "globalName";
var obj = {
name: "tyang",
getName: function() {
return this.name;
}
};
var getName = obj.getName;
console.log(obj.getName()); // tyang
console.log(getName()); // globalName
复制代码
obj.getName()
做为 obj 对象的属性被调用,this 指向 obj 对象;浏览器
getName()
使用变量 getName 引用 obj.getName,此时是函数调用方式,this 指向全局 window;闭包
在严格模式,状况有所不一样:this 不会指向全局对象,而是 undefined:app
function func() {
"use strict";
console.log(this); // undefined
}
func();
复制代码
当函数做为某个对象的方法调用时,this 等于那个对象。不过,匿名函数的执行环境具备全局性,所以其 this 对象一般指向 window。函数式编程
var gName = "The window";
var gObject = {
gName: "My object",
getName: function() {
return function() {
// 返回一个匿名函数
return this.gName;
};
}
};
console.log(gObject.getName()()); // 'The window'
var getNameFunc = gObject.getName();
console.log(getNameFunc()); // 'The window'
复制代码
建立了一个全局对象 gName
,这个对象包含一个方法 getName()
, 这个方法返回一个匿名函数,这个匿名函数返回 this.name
。所以调用 gObject.getName()()
会当即执行匿名函数,并返回一个字符串 'The window'
。函数
为何匿名函数没有取得包含做用域的 this 对象呢?学习
每一个函数再被调用的时候,会自动取得两个特殊变量:this 和 arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象为止,所以永远不可能直接访问外部函数中的这两个变量。ui
因此,能够在外部做用域中设置一个变量来保存 this 对象:
var gName = "The window";
var gObject = {
gName: "My object",
getName: function() {
var that = this; // 将 this 对象赋值给 that 变量
return function() {
return that.gName; // that 引用着 gObject
};
}
};
console.log(gObject.getName()()); // 'My object'
复制代码
固然,arguments 对象也能够如此使用:对该对象的引用保存到另外一个闭包可以访问的变量中。
当使用 new 运算符调用函数时,该函数会返回一个对象,通常状况下,构造器里的 this 指向返回的这个对象,如:
var MyClass = function() {
this.name = "Lisi";
};
var nameObj = new MyClass();
console.log(nameObj.name); // Lisi
复制代码
可是,当显式返回一个 object 类型的对象时,那最终会返回这个对象,并非以前的 this:
var MyClass = function() {
this.name = "Lisi";
return {
// 若是这里不会烦 object 类型的数据,如:return 'wangwu',就不会返回显式对象
name: "wangwu"
};
};
var nameObj = new MyClass();
console.log(nameObj.name); // wangwu
复制代码
call 和 apply 能够动态地改变传入函数的 this:
var personObj = {
name: "ytao",
age: "22"
};
function person() {
return this.name + this.age;
}
console.log(person.call(personObj)); // ytao22
复制代码
咱们通常会重写这个获取 id 的方法:
var getId = function(id) {
return document.getElementById(id);
};
getId("divBox");
复制代码
那可不能够这样呢:
getId2 = document.getElementById;
getId2("divBox"); // Uncaught TypeError: Illegal invocation
复制代码
结果直接报错,当 getElementById
方法做为 document 对象的属性被调用时, 方法内部的 this 是指向 document 的。若是 getId2('divBox')
,至关因而普通函数调用,函数内部的 this 指向的是 window。
因此,按照这个思路,咱们能够这样模拟一下它的实现:
document.getElementById = (function(func) {
return function() {
return func.apply(document, arguments);
};
})(document.getElementById);
getId3 = document.getElementById;
getId3("divBox");
复制代码
fun.apply(thisArg, [argsArray])
fun.call(thisArg, arg1, arg2, ...)
在函数式编程中,call 和 apply 方法尤其有用,二者用法一致,只是传参的形式上有所区别而已。
apply() 接受两个参数,第一个参数指定了函数体内 this 对象,第二个是数组或者类数组,apply() 方法将这个集合中的元素做为参数传递给被调用的函数。
call() 方法的做用和 apply() 方法相似,区别就是 call()方法接受的是参数列表,而 apply()方法接受的是一个参数数组。
第一个参数为 null,函数体内的 this 会指向默认的宿主对象,可是在严格模式下,依然是 null。
var applyFunc = function(a, b, c) {
console.log(this === window);
};
applyFunc.apply(null, [1, 2, 3]); // true
var applyFunc = function(a, b, c) {
"use strict";
console.log(this === null);
};
applyFunc.apply(null, [1, 2, 3]); // true
复制代码
假如在一个点击事件函数中有一个内部函数 func,当点击事件被触发时,就会出现以下状况:
document.getElementById("divBox").onclick = function() {
console.log(this.id); // divBox
var func = function() {
console.log(this.id); // undefined,这里的 this 指向了 window
};
func();
};
复制代码
这时,咱们用 call() 来改变一下:
document.getElementById("divBox").onclick = function() {
console.log(this.id); // divBox
var func = function() {
console.log(this.id); // divBox
};
func.call(this);
};
复制代码
function.bind(thisArg[, arg1[, arg2[, ...]]])
bind()方法建立一个新的函数,在调用时设置 this 关键字为提供的值。并在调用新函数时,将给定参数列表做为原函数的参数序列的前若干项。
Function.prototype.bind = function(context) {
var self = this; // 保存原函数
return function() {
// 返回新函数
return self.apply(context, arguments); // 将传入的 context 当作新函数体内的 this
};
};
var bindObj = {
name: "tyang"
};
var bindFunc = function() {
console.log(this.name); // tyang
}.bind(bindObj);
bindFunc();
复制代码
这是一个简化版的 Function.prototype.bind
实现,self.apply(context, arguments)
才是执行原来的 bindFunc 函数,而且指定 context 对象为 bindFunc 函数体内的 this。
咱们再继续修改下,使之能够预先添加一些参数:
Function.prototype.bind = function() {
var self = this,
context = [].shift.call(arguments), // 获取参数中第一个为绑定的this上下文
args = [].slice.call(arguments); // 将剩余的参数转化为数组
// 返回新函数
return function() {
return self.apply(context, [].concat.call(args, [].slice.call(arguments))); //arguments 为新函数的参数,即传入的 3,4
};
};
var bindObj = {
name: "lisisi"
};
var bindFunc = function(a, b, c, d) {
console.log(this.name); // lisisi
console.log([a, b, c, d]); // [1, 2, 3, 4]
}.bind(bindObj, 1, 2);
bindFunc(3, 4);
复制代码
self.apply(context, [].concat.call(args, [].slice.call(arguments)));
,执行新函数的时候,会把以前传入的 context 做为 this,[].slice.call(arguments)
将新函数传入的参数转化为数组,并做为[].concat.call(args)
的给定参数,组合两次,做为新函数最终的参数。
第一种,”借用构造函数“实现一些相似继承的效果:
var A = function(name) {
this.name = name;
};
var B = function() {
A.apply(this, arguments);
};
B.prototype.getName = function() {
return this.name;
};
var bbb = new B("Yangtao");
console.log(bbb.getName()); //Yangtao
复制代码
第二种,给类数组对象使用数组方法,好比:
(function() {
Array.prototype.push.call(arguments, 3);
console.log(arguments); // [1, 2, 3]
})(1, 2);
复制代码
再好比以前用到的,把 arguments 转成真正的数组的时候能够借用 Array.prototype.slice.call(arguments)
,想截去头一个元素时,借用Array.prototype.shift.call(arguments)
虽然咱们能够把”任意“对象传入 Array.prototype.push
:
var aObj = {};
Array.prototype.push.call(aObj, "first");
console.log(aObj.length); // 1
console.log(aObj[0]); // first
复制代码
可是,这个对象也得知足如下两个条件:
若是是其余类型,好比 number,没法存取;好比函数,length 属性不可写,使用 call 或 apply 就会报错:
var num = 1;
Array.prototype.push.call(num, "2");
console.log(num.length); // undefined
console.log(num[0]); // undefined
var funcObj = function() {};
Array.prototype.push.call(funcObj, "3");
console.log(funcObj.length); // Uncaught TypeError: Cannot assign to read only property 'length' of function 'function () {}'
复制代码
学习资料: