什么是this
- this是JavaScript语言中的关键字。也叫全局上下文对象。是在函数运行时自动在内部生成的一个对象
this指向的几种情况
1.在全局运行环境中(在任何函数体的外部),this指向的是全局对象window
console.log(this === window) // true
this.b = "web"
console.log(window.b) // "web"
2.在函数内部,this指向取决于函数的调用方式
- 我们来看一段代码
var name = "资讯";
var obj = {
name: "沸点",
say: function () {
console.log(this.name);
},
};
在上述代码中,我们定义了一个name变量和一个obj对象,对象里有一个say方法,那么我们接下来要通过调用方式的不同看看say方法里面的this.name到底指向的是哪个name
Ⅰ. 直接调用法(这个时候this指向的是该方法的拥有者)
obj.say() // returns "沸点"
Ⅱ. 普通函数调用法(这个时候this指向的是全局window对象)
var mySay = obj.say;
mySay() // returns "资讯"
Ⅲ. 作为构造函数被调用(这个时候this指向的就是这个新对象)
var name = "掘金";
var obj = {
name: "资讯",
say: function () {
this.name = "沸点";
},
};
var osay = new obj.say();
// 这个函数会立即执行,但是执行过后并没有改变全局window的name,所以这时候的this指向的是new实例过后生成的新对象
console.log(name); // returns '掘金'
console.log(osay.name); // returns '沸点'
3.通过call apply bind进行调用
首先我们要明确,这三个方法都是用来改变this指向的,那么三者有什么区别呢?
- call方法接受的第一个参数是this的指向,后面传入的是一个参数列表。当一个参数为null或undefined的时候,表示指向window(在浏览器中),call只是临时改变一次this指向,并立即执行。
var fn = function (age, sex) {
console.log(
"我叫" + this.name + ",我今年" + age + "岁了!" + "我是" + sex
);
};
var obj = {
name: "二狗子",
};
// call 方法接受的第二个参数可以一个一个的传
fn.call(obj, 24, "男生"); // '我叫二狗子,我今年24岁了!我是男生'
- apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),call只是临时改变一次this指向,并立即执行。
var fn = function (age, sex) {
console.log(
"我叫" + this.name + ",我今年" + age + "岁了!" + "我是" + sex
);
};
var obj = {
name: "二狗子",
};
// apply 方法接受的第二个参数是一个数组
fn.apply(obj, [24, "男生"]);// '我叫二狗子,我今年24岁了!我是男生'
- bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
var fn = function (age, sex) {
console.log(
"我叫" + this.name + ",我今年" + age + "岁了!" + "我是" + sex
);
};
var obj = {
name: "杨泽",
};
// 参数可以分多次传入
const o_bind = fn.bind(obj, 24);
o_bind("男生"); // '我叫二狗子,我今年24岁了!我是男生'
手动实现call,apply,bind
1.实现call方法
Function.prototype.myCall = function (thisArg, ...rest) {
// 对传入的参数进行判断
if (typeof thisArg === "object") {
// 如果传入为null的话就指向window
thisArg = thisArg || window
} else {
// 类型不正确就创建一个新对象
thisArg = Object.create({});
}
// 创建一个唯一的值
const fn = Symbol("fn");
// 通过改变调用方式的方法改变this指向
// this 就是调用myCall方法的函数,thisArg就是this要指向的对象
thisArg[fn] = this;
// 在当前对象的作用域下面执行这个函数,就改变了这个函数的this指向,同时把剩余参数传过去
const result = thisArg[fn](...rest);
delete thisArg[fn]; // 最后把这个属性删除,因为本身这个对象里是不需要这个属性的
return result
};
const obj = {
name: "二狗子",
};
function Fn(age, sex) {
this.age = age;
this.sex = sex;
console.log(this.name, age, sex);
}
Fn.myCall(obj, 24, "男"); // 二狗子 24 男
2.实现apply方法
Function.prototype.myApply = function (context, args) {
// 首先判断第一个参数是不是正确的格式
if (typeof context === "object") {
context = context || window;
} else {
context = Object.create({});
}
// 创建一个唯一的值
const fn = Symbol("fn");
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
const obj = {
name: "二狗子",
};
function Fn(age, sex) {
this.age = age;
this.sex = sex;
console.log(this.name, age, sex); // 二狗子 18 男
}
Fn.myApply(obj, [18, "男"]);
3.实现apply方法
Function.prototype.myBind = function (context, ...args) {
// 把函数先保存起来
const me = this;
// bind不是立即调用,所以需要再返回一个函数
return function (...arg) {
// 利用call方法
return me.call(context, ...args, ...arg);
};
};
const obj = {
name: "二狗子",
};
function Fn(age, sex, address) {
this.age = age;
this.sex = sex;
this.address = address;
console.log(this.name, age, sex, address); // 二狗子 18 男 山西
}
const bindFn = Fn.myBind(obj, 18, "男");
// 参数可以分为两次传入
bindFn("山西");
结语
就是单纯的做一些总结和学习,如果您看到有哪不正确或者不完整的还希望能指出来,大家一起学习,共同进步!