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(); //jimmygetName(); //jimmy
上面两种写法是完全等价的,只是一般比较懒,直接写成下面那一种了。
作为普通函数调用
上面已经说了,普通函数的this的不指定性,即,在使用之前函数的this都是不定的,直到函数执行的时候。
在es5中规定,当你的函数在全局中执行,该this会指向window.如果在严格模式下,则this为undefinedfunction 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指向的是该元素的引用
123//jsdocument.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(); //jimmyget.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~