持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情 什么是this
this是在函数的内部使用,用来指代当前的运行环境
global this
1、this等价于window对象;
2、使用var定义的变量就相当于window定义变量
3、声明变量时没有使用var或者let、const的活,变量就是指向全局的
总结: 在全局范围里的this就相当于window对象(this=window)
function this
函数的this最好的理解就是在运行时this永远指向最后调用它的那个对象。
var name = "windowName"
function Animal(){
var name = 'tiger';
console.log(this.name); // windowName
console.log(this); // window
}
Animal()
console.log(this); //window
从上面的例子可以看到Animal()并没有调用的对象,所以this指向全局的window
var name = "windowName"
function Animal(){
var name = 'tiger';
console.log(this.name);
console.log(this);
}
let obj = {
name: 'obj',
animal: Animal
}
obj.animal() // obj
当我用obj的animal去调用animal方法时this就会指向obj
构造函数中的this
构造函数就是通过这个函数生成一个新的对象(object)。当函数作为构造器时,this指向新创建的对象。注意:如果没使用new关键字,那么就只是个普通函数,this将指向window对象
(总结一句话:谁被new了,this就指向谁)
class中的this
类通常包含constructor,this可以指向任何新创建的对象,this可以指向任何新创建的对象,不过在作为方法时,如果该方法作为普通函数被调用,this也可以指向任何其他值。与方法相同,类也可能失去对接收器的跟踪
call、apply和bind中的this
call、apply、bind被称为this的强绑定,用来改变函数执行时的this指向,目前所有关于它们的运用都是基于这点进行的
var name = 'xunCai'
function hei(){
console.log(this.name); //xunCai
}
var obj = {
name: 'kung'
}
hei()
hei.call(obj) // kung
hei.apply(obj) //kung
hei.bind(obj) //kung
箭头函数的this
箭头函数是没有this绑定的,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包裹,则this绑定的是最近一层非箭头函数的this,否则this为undefined,箭头函数的this始终指向函数定义时的this,而非执行时。
let name = 'kk'
let s = {
name: 'tt',
hei: function(){
console.log(this.name);
},
func: function(){
setTimeout(()=>{
this.hei()
},100)
}
}
s.func() // tt
tips: 使用call、apply或bind等方法给this传值,箭头函数会忽略。箭头函数引用的是箭头函数在创建时设置的this值
面试题
var number = 1;
var obj = {
number:2,
showNumber:function(){
this.number = 3;
(function(){ console.log(this.number); })(); // 1 自调用是种特殊情况,自调用会将this指向window对象
console.log(this.number); 3
、 }
};
obj.showNumber();
call & apply
每个函数都包含两个非继承而来的方法:apply()和call。都是用于特定的作用域中调用函数,实际上等于设置函数体内this对象的值
apply()
apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。
function count(num1,num2){
return num1+num2;
}
function callSum1(num1,num2){
return count.apply(this,arguments);
}
function callSum2(num1,num2){
return count.apply(this,[num1,num2]);
}
console.log(callSum1(10,10));
console.log(callSum2(10,10));
tips:严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()或者call(),否则this值将是undefined。
call()
call()方法与apply()方法的作用相同,它们的唯一区别在于接收函数的方式不同。在使用call()方法时,传递给函数的参数必须逐个列举出来。
function count(num1,num2){
return num1+num2;
}
function callSum(num1,num2){
return count.call(this,num1,num2);
}
console.log(callSum(10,10));
call()方法与apply()方法返回的结果是完全相同的,至于是使用apply()还是call(),完全取决于采用哪种给函数传递参数的方式最方便。
参数数量/顺序确定就用call,参数数量/顺序不确定的话就用apply。
考虑可读性:参数数量不多就用call,参数数量比较多的话,把参数整合成数组,使用apply。
bind()
bind()方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。bind()会返回一个新函数。
window.color = 'red';
var c = { color: 'blue' };
function sayColor(){
alert(this.color)
}
var objectSayColor = sayColor.bind(c);
objectSayColor();
call/apply与bind的区别
执行:
call/apply改变了函数的this上下文后马上执行该函数
bind则是返回改变了上下文后的函数,不执行该函数
function count(a,b){
return a+b;
}
function sub(a,b){
return a-b;
}
console.log(count.bind(sub,5,3)); // 返回函数
console.log(count.bind(sub,5,3)()); //立即执行
返回值
call/apply返回fun的执行结果
bind返回fun的拷贝,并指定了fun的this指向,保存了fun的参数
手写实现apply、call、bind
apply
1、先给Function原型上扩展个方法并接受2个参数
Function.prototype.myApply = function (context,args) {}
2、因为不传context的话,this会指向window,所以这里将context和args做一下容错处理。
Function.prototype.myApply = function (context,args){
context = (typeof context === 'object'?context:window)
args = args ? args:[]
}
3、使用隐式绑定去实现显示绑定
Function.prototype.myApply = function (context,args){
context = (typeof context === 'object'?context: window)
args = args?args:[]
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol()
context[key] = this
//通过隐式绑定的方式调用函数
context[key](...args)
}
4、最后一步要返回函数调用的返回值,并且把context上的属性删除避免造成影响
Function.prototype.myApply = function (context,args){
// 处理容错
context = ( typeof context === 'object'?context:window )
args = args?args:[]
//给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol();
context[key] = this;
// 通过隐式绑定的方式调用函数
const result = contextkey;
// 删除添加的属性
delete context[key]
// 返回函数调用的返回值
return result;
}
function func(...args){
console.log(this.name,...args);
}
const result = {
name: 'Jake'
}
func.myApply(result,[1,2])
call
// 传递参数从一个数组变成逐个传参,不用...扩展运算符的也可以用arguments代替
Function.prototype.myCall = function(context,...args){
// 这里默认不传就是window,也可以用es6给参数设置默认参数
context = ( typeof context === 'object' ? context : window )
args = args ? args : []
// 给context新增一个独一无二的属性以免覆盖原有属性
const key = Symbol();
context[key] = this;
// 通过隐式绑定的方式调用函数
const result = contextkey;
// 删除添加的属性】
delete context[key];
// 返回函数调用的返回值
return result;
}
bind
Function.prototype.myBind = function (objThis,...params){
const thisFn = this; // 存储源函数以及上方的params(函数参数)
// 对返回的函数 secondParams 二次传参
let fToBind = function (...secondParams){
const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
return thisFn.call(context,...params,...secondParams);
}
if(thisFn.prototype){
// 复制源函数的prototype给fToBind一些情况下函数没有prototype,比如箭头函数
fToBind.prototype = Object.create(thisFn.prototype);
}
return fToBind; 返回拷贝函数
}
总结
1、在浏览器里,在全局范围内的this指向window对象
2、在函数中,this永远指向最后调用他的那个对象
3、构造函数中,this指向new出来的那个新的对象
4、call、apply、bind中的this被强绑定在指定的那个对象上
5、箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this,要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来
6、apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参
\