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~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。