WEB前端 -- Javascript中的this

  1. this是什么--基于调用位置的上下文调用位置不同this值不同
  1. 二为什么使用this
  1. this的四大绑定规则
    1. 默认绑定--函数调用类型独立函数调用this指向全局对象
    1. 隐式绑定--调用位置是否有上下文对象或者说被某个对象拥有或者包含
    1. 显示绑定
    1. new绑定
  1. 四优先级
  1. 五绑定this注意点
    1. 忽略this
    2. 间接引用
    3. ES6箭头函数
  1. 六详解如何确定this
    1. 隐式绑定如果某个对象中某个成员是个function当从这个对象上调用这个方法时this指向当前对象
    1. 隐私绑定可以通过创建一个新的对象来引用FenFei对象上的timeTravel方法
    2. 隐式丢失使用变量保存FenFeitimeTravel方法的引用
    3. 异步调用的方法内部的this
    4. new绑定构造函数里的this
    5. 显示绑定callapply方法设定this
    6. 显示绑定bind将函数绑定至某个对象ES5
  1. 七补充
    1. 使用new为函数创建多个实例的时候这些实例会共享prototype
    2. 柯里化

JavaScript中的this,刚接触JavaScript时大家都在大肆渲染说其多么多么的灵巧重要,然而自己并不关心;随着自己对JavaScript一步步深入了解,突然恍然大悟,原来它真的很重要!所以,自己花费了大约2周的时间去查贴、翻阅之前读的书籍,将this的全貌展示如下。

一、this是什么--基于调用位置的上下文;调用位置不同,this值不同。

大家都JavaScriptthis存在两个误解:

1this指向函数自身

2this指向函数的作用域

作用域无法通过JavaScript代码访问,它存在于JavaScript引擎内部。每当把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的!

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用位置(也就是函数的调用方式)!

示例:

var foo = "golbal foo";
var myObj = {foo : 'myObj foo'};
var say = function(){
console.log(this.foo);
}
myObj.say = say;
myObj.say(); //结果:myObj foo
say(); //结果:golbal foo

二、为什么使用this

var me = {
name: "fenfei"
};
//不使用this,调用
function speak(name){
console.log("Hello, I'm "+ name);
}
speak(me.name); //Hello, I'm fenfei
//使用this,调用
function speak(){
console.log("Hello, I'm "+ this.name);
}
speak.call(me); //Hello, I'm fenfei

this提供了一种更优雅的方式来隐式“传递”对象引用,因此可以将API设计得更加简洁并易于复用。

. this的四大绑定规则

1.默认绑定--函数调用类型:独立函数调用,this指向全局对象。

var a = "foo";
function foo(){
console.log(this.a);
}
foo(); // "foo"
 
在严格模式下,全局对象将无法使用默认绑定,因此this会绑定到undefined。
var a = "foo";
function foo(){
"use strict";
console.log(this.a);
}
foo(); // TypeError:this is undefined

2.隐式绑定--调用位置是否有上下文对象,或者说被某个对象拥有或者包含

function foo(){
console.log(this.a);
}
var obj1 = {
a : 2,
foo : foo
}
var obj2 = {
a : 1,
obj1 : obj1
}
obj2.obj1.foo(); //结果:2

foo()被调用时,它的落脚点指向obj1对象,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

对象属性引用链中只有最顶层或者说最后一次层会影响调用位置。

注意:隐式丢失

常见的this绑定问题就是“隐式绑定”的函数会丢失绑定对象,也就是“默认绑定”,从而把this绑定到全局对象(严格模式下为undefined)。

var a = "foo";
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
}
var bar = obj.foo;
bar(); //"foo"

虽然barobj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是不带任何修饰的函数调用,因此应用了默认绑定。

var a = "foo";
function foo(){
console.log(this.a);
}
function doFoo(fn){ //var fn = obj.foo
fn();
}
var obj = {
a : 2,
foo : foo
}
doFoo(obj.foo); //"foo"
setTimeout(obj.foo, 100); //"foo"

参数传递其实就是一种隐式赋值,因此传入函数式会被隐式赋值(LHS

3.显示绑定

1callapply

2)硬绑定

 

创建了函数bar(),在其内部手动调用了foo.call(obj),因此强制把foothis绑定到了obj上。无论之后如何调用函数bar,总会手动在obj上调用foo。这种显示的强制绑定,称为“硬绑定”。

4. new绑定

new调用函数会自动执行下面操作:

1)创建(或者说构造)一个全新的对象;

2)这个新对象会被执行[[原型]]连接;

3)这个新对象会绑定到函数调用的this

4)如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

四、优先级

了解了函数调用中this绑定的四条规则,需要做的就是找到函数的调用位置并判断对应哪条规则。

1. 函数是否是new绑定?如果是,this绑定的是新创建的对象。

var bar = new Foo();

2.函数是否通过callapply显示绑定或硬绑定?如果是,this绑定的是指定的对象。

var bar = foo.call(obj);

3.函数是否在某个上下文对象中隐式调用?如果是,this绑定的是那个上下文对象。

var bar = obj.foo();

4.上述全不是,则使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局window对象。

var bar = foo();

五、绑定this注意点

1.忽略this

nullundefined作为this的绑定对象传入callapplybind,调用时会被忽略,实际应用的是默认绑定规则!

function foo(){
console.log(this.a);
}
var a = 1;
foo.call(null, 2); //1
foo.apply(undefined, [3]); //1

2.间接引用

function foo(){
console.log(this.a);
}
var a = 2;
var o = { a : 3,foo : foo};
var p = { a : 4};
o.foo(); //3
(p.foo = o.foo)(); //2间接引用
var pfoo = o.foo;
pfoo(); //2隐式丢失

注意:同上述“隐式丢失”结果一样,但是过程不太一样,区分!!

3. ES6箭头函数

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this

箭头函数的绑定无法被修改。常用于回调函数中,如事件处理器或定时器。和ES6之前代码中的this = self机制一样。

function foo(){
setTimeout(()=>{
console.log(this.a);
},100);
}
var obj = { a : 2};
foo.call(obj);

等价于:

function foo(){
var self = this;
setTimeout(function(){
console.log(self.a);
},100);
}
var obj = { a : 2};
foo.call(obj);

六、详解如何确定this

1.(隐式绑定)如果某个对象中某个成员是个function,当从这个对象上调用这个方法时this指向当前对象。

var FenFei = {
firstname:"li",
lastname:"gang",
timeTravel:function(year){
console.log(this.firstname + " " + this.lastname + " is time traveling to " + year);
}
}
FenFei.timeTravel(2014); //li gang is time traveling to 2014(父/拥有者对象:FenFei)

2.(隐私绑定)可以通过创建一个新的对象,来引用FenFei对象上的timeTravel方法。

var Camile = {
firstname:"li",
lastname:"yunxia"
}
Camile.timeTravel = FenFei.timeTravel;
Camile.timeTravel(2014); //li yunxia is time traveling to 2014(父/拥有者对象:Camile)

注意:此示例同上述“隐式丢失”、“间接引用”区分!!!

3.(隐式丢失)使用变量保存FenFei.timeTravel方法的引用

var getTimeTravel = FenFei.timeTravel;
getTimeTravel(2014); //undefined undefined is time traveling to 2014(父/拥有者对象:Window;window对象里并没有firstName和lastName属性)

PS:谨记方法中的this将指向调用它的那个父/拥有者对象!无论何时,当一个函数被调用,我们必须看方括号或者是圆括号左边紧邻的位置,如果我们看到一个引用(reference),那么传到function里面的this值就是指向这个方法所属于的那个对象,如若不然,那它就是指向全局对象的。

4.异步调用的方法内部的this

<buttonid="async">点我</button>

var btnDom = document.getElementById("async");
btnDom.addEventListener('click',FenFei.timeTravel);//undefined undefined is time traveling to [object MouseEvent](父/拥有者对象:button)
btnDom.addEventListener('click',function(e){
FenFei.timeTravel(2014);//li gang is time traveling to 2014(父/拥有者对象:FenFei)
});

5.new绑定)构造函数里的this

当使用构造函数创建一个对象的实例时,构造函数里的this就是新建的实例。

var TimeTravel = function(fName, lName){
this.firstname = fName;
this.lastname = lName;
}
var FenFei = new TimeTravel("li", "gang");
console.log(FenFei.firstname + " " + FenFei.lastname); //li gang

6.(显示绑定)callapply方法设定this

var Camile = {
firstname:"li",
lastname:"yunxia"
}
FenFei.timeTravel.call(Camile,2014); //li yunxia is time traveling to 2014(指定this对象为Camile)
FenFei.timeTravel.apply(Camile,[2014]); //li yunxia is time traveling to 2014(指定this对象为Camile)

PS:注意和上述“2”处做对比

7.(显示绑定)bind将函数绑定至某个对象(ES5

function f(y){
return this.x + y;
}
var o = {x:1};
/* f.bind(o)返回一个新函数,调用g(2)会把原始的函数f()当作o的方法来调用 */
var g = f.bind(o);
g(2); // 3

补充:ES5之前方法模拟bind()方法

function myBind(f, o){
if(f.bind){
return f.bind(o);
}else{
return function(){
return f.apply(o, arguments);
}
}
}

bind的作用和applycall类似都是改变函数的execute context,也就是runtimethis关键字的指向。但是使用方法略有不同。一个函数进行bind后可稍后执行。

七、补充

1.使用new为函数创建多个实例的时候,这些实例会共享prototype

当在一个实例里直接给this添加属性的时,会隐藏prototype中与之同名的属性。

如果想访问prototype中的属性值而不是自己的设定的属性值:

1)删除实例自己添加的属性: delete实例名.属性名

2)直接访问prototype中的属性:Thing.prototype.name

function Thing(){}
Thing.prototype.name = "leegang";
Thing.prototype.getName = function(){
console.log(this.name);
}
Thing.prototype.setName = function(newName){
this.name = newName;
}
Thing.prototype.deleteName = function(){
delete this.name;
}
var thing = new Thing(); /*thing:Thing {}*/
thing.setName("liyunxia"); /*thing:Thing {name: "liyunxia"}*/
thing.getName(); //结果:liyunxia
thing.deleteName(); /*thing:Thing {}*/
thing.getName(); //结果:leegang thing.__proto__为Thing {name: "leegang"}
thing.name = "liyunxia"; /*Thing {name: "liyunxia"}*/
thing.getName(); //结果:liyunxia
delete thing.name; /*thing:Thing {}*/
thing.getName(); //结果:leegang thing.__proto__为Thing {name: "leegang"}

2.柯里化

把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数

var sum = function(x, y){
return x + y;
}
var succ = sum.bind(null, 1);
succ(2); //3
function f(y, z){
return this.x + y +z;
}
var g = f.bind({x:1}, 2);
g(3); //6 

源文档 <http://blog.csdn.net/ligang2585116/article/details/47059289

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值