this的使用

关于js里面有哪些难点,艹,js里面全是难点。。。什么闭包,原型,函数,对象,类型检测,this。。。但是作为一名正统的前端爱好者而言---这点痛算什么。今天我们来解决this这个点。由于牵扯到this,则必定会牵扯到函数,因为没有函数就没有this的存在的意思。
话不多说,Cut~

this的用法通常可以分为:

1. 作为对象的方法调用
2. 作为普通函数调用
3. 构造器调用
4. call,apply调用

作为对象方法调用

var obj = {
    name :"jimmy",
    getName(){
        return this.name;
    }
}
console.log(obj.getName());  // jimmy

这里的this指向的是该对象.由于函数里面this是在运行时指定的,所以有一个诀窍就是,函数里面的this,指的是使用"."调用函数的所有者。

document.querySelector("#jimmy").onclick = function(){
    console.log(this.tagName);  
}

上面的this指的就是document.querySelector("#jimmy")。还有一种情况,当你使用的时候前面什么都没加,则这是函数中的this是global对象---window. 那上面的法则不是不对吗? 其实,在window里面调用函数,可以这样写.

function getName(){
    console.log("jimmy");
}
window.getName();  //jimmy
getName();  //jimmy

上面两种写法是完全等价的,只是一般比较懒,直接写成下面那一种了。

作为普通函数调用

上面已经说了,普通函数的this的不指定性,即,在使用之前函数的this都是不定的,直到函数执行的时候。
在es5中规定,当你的函数在全局中执行,该this会指向window.如果在严格模式下,则this为undefined

function name(){
    console.log(this);
}
console.log(name());  // undefined

通常,当你的函数作为普通函数被调用的时候,this指向的是window,这个已经说过了~

function getName(){
    console.log(this.name);
}
var obj = {
    name : "jimmy",
    getName:getName
}
obj.getName();  //jimmy

在事件执行的时候,this指向的是该元素的引用

<div class="name">123</div>
//js
document.querySelector('.name').onclick = function(){
  console.log(this);
}
//得到的结果是.[object HTMLDivElement]

同样,加深印象。

构造器的调用

这其实很简单,就是使用new + funciton 创建的是一个对象,而不是一个函数.

function GetName(){
    this.name = "jimmy";
}
GetName.prototype.getName = function(){
    return this.name;
}
var get = new GetName();
get.name(); //jimmy
get.getName(); //jimmy

这里的this指向的就是getName.prototype上的。
但需要注意在构造器内不能显示的返回一个对象,否则你的实例化就会被破坏。

function GetName(){
    this.name = "jimmy";
    return {};  //显示返回一个对象
}
GetName.prototype.getName = function(){
    return this.name;
}
var name = new GetName();
console.log(name);  //{}
var name2 = GetName();
console.log(name2); //{}
//显示返回一个非对象
function GetName(){
    this.name = "jimmy";
    return "sam";  //返回非对象
}
GetName.prototype.getName = function(){
    return this.name;
}
var name = new GetName();
console.log(name); //["Object Object"]
var name2 = GetName();
console.log(name2);  //sam

call,apply调用

call和apply的区别,一个是传入参数为枚举,一个为数组
实际上,他们的用处还有扩展了函数的作用域

function getName(){
    console.log(this.name);
}
var obj = {
    name : "jimmy"
}
getName.call(obj);  //jimmy
//相当于
obj.getName = getName;
obj.getName();  //这一个执行过程

使用call || apply改变this的作用域是非常关键的.
this的丢失
所谓的this丢失一般指的就是函数中this的丢失. 因为函数中的this只有在执行的时候才会确定指向。

var getId = document.getElement;
getId("#name");  //这里会报错

上面的错误主要是因为,使用document.getElement时,this是指向document,在getElement里面会需要使用this上面的一系列方法,而上面的方式则和普通调用函数的方式一样,这时this的指向是全局的global,所以会造成有些方法使用不到. 这里可以改进:

var getId = (function(fn){
    return funciton(){  //返回函数
        return fn.apply(document,arguments);
    }
})(document.getElement);  //保存引用
console.log(getId("#name"));  

但其实上面写法还不如直接return好使.这里只是方便讲解.
而es5里面的bind函数应该算是一个将call和apply用到了点子上的方法。

var getName = function(){
  console.log(this.tagName);  //DIV
  var sam = function(){
    console.log(this.tagName);  //undefined
  }
  sam();
}
document.querySelector('.name').onclick = getName;

上面的问题其实已经说到过了,就是出在函数作为普通函数调用的时候,里面的this永远指向的是 被赋予的对象。比如sam。 上面getName函数在执行的时候this是指向document.querySelector('.name')的.所以不会有什么问题.
改进的办法就是将this保存作用域(闭包).

var getName = function(){
  var _this = this;
  var sam = function(){
    console.log(_this.tagName);  //DIV
  }
  sam();
}
document.querySelector('.name').onclick = getName;

由于闭包的长相完全是芙蓉姐姐,所以在es6中为了避免闭包的露脸加上了 箭头函数的feature.

var getName = function(){
  var sam =()=>{
    console.log(this.tagName);
  }
  sam();
}
document.querySelector('.name').onclick = getName;  //DIV

完美输出. 虽然这样会显得你比较牛逼,但是如果你把这个投入生产,保证leader分分钟切si 你。 md这么长.
这时候apply就完美登场了。

var getName = function(){
    console.log(this.tagName);
}
document.querySelector('.name').onclick = function(){
  getName.apply(this);
};

是不是感觉少了很多代码~ 其实还可以再次改进,使用bind.

document.querySelector('.name').onclick = getName.bind(document.querySelector('.name'));

但这样其实还不如使用apply. 还需要注意的是call和apply都会直接执行函数,而bind只是返回一个函数.
apply和call的用法还有一个,就是借用其他对象的方法.
比如Math.max();函数,本来他只能接受枚举的参数,但可以使用apply进行装换

var num = [1,2,3,4,3,22];
console.log(Math.max.apply(null,num));  //22

上面基本说明了apply和call的用处,但事实上,只要你用得好,apply和call的价值应该灰常大的。比如你还可以使用[].slice.call(arguments),用来将arguments类数组对象转化为一个真正的数组。
Ending~


villainhr
7.8k 声望2.2k 粉丝