this的重要性不言而喻,比如面试题经常考到,其次,如果彻底理解了this,那么对理解框架源码及编写高质量代码都有很大的帮助。本文就是要深入剖析this的几种情况,理解了原理,以后妈妈再也不用担心你的this了。。

  this是动态绑定的,其实相对应的是作用域,因为作用域是在代码刚刚写完的时候,就已经定义好了。理解了作用域,对理解闭包很有帮助。本文主要讲解this绑定,大家心里先有和作用域的一个大致对比就行,以后的文章会专门讲解。

  所谓动态绑定就是只有在函数被调用的时候,this才能确定它真正指向的是哪个对象。

  this分为以下四种情况,这四种掌握了,就打遍天下无敌手了~

  我们先定义一个函数: 

function foo(name){
   this.name=name;
   console.log(this.name);
}

 一、new绑定

 var obj=new foo("yx");

  控制台输出结果:"yx"

  继续在控制台输入: obj.name  ,结果依然是"yx"

  对于上面的 foo 函数,

  如果改成

  function foo(name){
  var o={name:"peggy"};
  this.name=name;
  console.log(this.name);
  return o;
  }
  var obj=new foo();

  控制台输出结果依然是"yx"

  继续在控制台输入 obj.name ,发现结果竟然变成了"peggy"!

  其实我觉得原因大家已经猜出来了:

  如果 foo 函数返回了一个对象,那么 obj 等于返回的那个对象o,如果没返回对象的话,则默认是 return this ;

  可能有些小伙伴会认为 foo 应该大写,而且 foo 是构造函数,其实压根没有什么构造函数,函数名大写也只是一种约定而已,其实真实的情况是使用 new 关键字构造调用了 foo 函数!那么此时的 this 在 foo 函数被调用的过程中,绑定到了一个新创建的对象上,如果没有指定对象返回的话,那么返回的就是这个新创建的对象。这个新创建的对象的内部 [[proto]] 属性会指向 foo.prototype ,这就涉及到原型与继承问题了,以后再讨论。

  所以结果为什么这样就很清晰了:两次调用 console.log(this.name) 会返回 "yx" 是因为 this 就是新创建的这个对象。而最后 obj.name 不一样,是因为函数返回的对象不一样了,所以此时 obj 代表的对象也就不一样了。

  二、显式绑定

  所谓的显示绑定有三种: call , apply , bind

  我们定义函数:

 function person(age, hobby){
  this.age=age;
  this.hobby=hobby;
  console.log(this.name);
  console.log(this.age);
  console.log(this.hobby);
 }

  因为函数也是对象,所以也是有方法的,每个函数其实可以理解为是由 Function  函数通过new构造调用的,即  new Function()

  所以每一个函数内部 [[proto]] 属性都指向  Function.prototype ,所以可以继承 Function.prototype 中的方法,比如; call , apply , bind

  call 方法和 apply 方法的共同点是:第一个参数代表 this 指向的对象

  不同点是: call 方法从第二个参数开始,传递的都是 person 函数需要的对应的参数,挨个挨个的传递

        apply 方法的第二个参数,传递的是一个数组,数组中的每一项与 person 函数需要的参数对应

  (1)call  
   var obj={name:"yx"};
  person.call(obj,24,"singing");

  此时 this 指向 obj ,所以控制台输出结果是"yx",24,"singing'

  (2)apply 
  var obj1={name:"peggy"};
  person.apply(obj1,[24,"running"]);

  此时, this 指向 obj1 ,控制台输出结果是"yx",24,"running"

  使用 apply 还可以用来展开数组,第二个参数传递 arguments 数组( arguments 数组代表函数实际传递的参数)

  比如:继承会用到

  var obj2={name:"xixi"};
  function student(age,hobby){
  person.apply(this,arguments);
  }

   student.call(obj2,24,"singing");

  此时 student 函数中的 this 指向传入的 obj2 , arguments 是一个类数组,代表传递的实参,

  所以如果想实现继承 person 函数的属性或者方法,可以用 apply 展开 arguments

  (3) bind
   var obj3={name:"nancy"};
   var obj4={name:"mingming"};
   var newFn=person.bind(obj3);

  调用 bind 函数会返回一个新函数,新函数的 this 会绑定在 obj3 上,无论以后是怎么调用 newFn , this 都会一直绑定在 obj3

  比如在控制台输入: newFn.call(obj4,24,"singing") 或者输入 newFn(24,"singing") 都不能改变this的绑定

  发现结果依然是输出:"nancy",而不是"mingming"

  bind 还可以用来进行科里化,就是可以先传参数进去,比如:

  var newFn1 = person.bind(obj3,24);
  newFn1("singing");
  newFn1("running");

  相当于先传了一个参数,之后调用的时候再传另外的参数就好。 

  不是所有的函数都有 protoype ,用 bind 创建的函数就没有 prototype 。比如: 

function fn(){return this.x}; var boundFn=fn.bind({x:1})

  那么 boundFn 就是没有 prototype 属性的。

  三、隐式绑定 

  function foo(){
  console.log(this.name)
  }
  var obj={
    name:"yx",
    fn:foo
  }
  obj.fn();

  控制台结果:"yx"

  此时 this 指向 obj , obj 为此时的上下文环境

  四、默认绑定:

  var name="yx";
  function foo(){
    var name="peggy";
    console.log(this.name);
  }
  foo();

  控制台结果:"yx"

  此时: this 指向 global 对象

  (1)浏览器环境中: this=window

  (2)node开发环境中: this=global

  其实上面四种情况的顺序,我是刻意这样安排的,因为从上到下优先级依次降低。

  根据这几种不同的调用方式, this 指向的对象是不同的。

  依次判断 new绑定-> 显式绑定-> 隐式绑定->默认绑定

  大家可以亲自去验证下,是不是这个顺序。

  牛刀小试:

(1)

function a(xx){this.x=xx;return this};
var x=a(5);var y=a(6);
console.log(x.x);
console.log(y.x);
(2)
var obj = {
x: 1,
y: 2,
t: function() {
console.log(this.x)
}
}
obj.t();
var dog={x:11};
dog.t=obj.t;
dog.t();
show=function(){
console.log('show'+this.x);
}
dog.t=show;
dog.t();
(3)
var number=2;
var obj={
number:4,
fn1:(function(){
var number;
this.number*=2;// number=number*2;//NaN
number=3;
return function(){
var num=this.number;
this.number*=2;//8 this=window
console.log(num);
number*=3;//9为了混淆,利用了闭包
alert(number);
}//这是个闭包
})(), db2:function(){
this.number*=2;
}
}
var fn1=obj.fn1;
alert(number);
fn1();//this=window
obj.fn1();//this=obj
alert(window.number);
alert(obj.number);

javascript之深入剖析this的更多相关文章

  1. js对象详解(JavaScript对象深度剖析,深度理解js对象)

    js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕 ...

  2. JavaScript实战实例剖析——(激励倒计时日历)

    如今JavaScript在前端开发中的地位越来越高,掌握JavaScript的深度往往能决定你职业道路深远,这次通过制作 带着倒计时功能的激励日历的小实例,进一步细致的掌握JavaScript的语法与 ...

  3. Javascript闭包概念剖析

    某种情况下,函数调用依然持有对其原始定义的作用域的引用,这个引用就叫做闭包. function foo(){ var a = 2; function bar(){ console.log(a); } ...

  4. 【repost】学JS必看-JavaScript数据结构深度剖析

    JavaScript以其强大灵活的特点,被广泛运用于各种类型的网站上.一直以来都没怎么好好学JS,只是略懂皮毛,看这篇文章时有读<PHP圣经>的感觉,作者深入浅出.生动形象地用各种实例给我 ...

  5. 学JS必看-JavaScript数据结构深度剖析

    回归简单 要理解JavaScript,你得首先放下对象和类的概念,回到数据和代码的本原.前面说过,编程世界只有数据和代码两种基本元素,而这两种元素又有着纠缠不清的关系.JavaScript就是把数据和 ...

  6. JavaScript instanceof深度剖析以及Object.prototype.toString.call()使用

    本文由segementfalt上的一道instanceof题引出: var str = new String("hello world"); console.log(str ins ...

  7. Javascript基本类型回顾

    本文是学习和总结ECMAScript5.1规范形成的.是对规范中所提及的Javascript类型进行剖析后的个人观点的表达(如有Bug望各位道友指正).主要是各类型的实例方法,不包含任务构造函数的方法 ...

  8. 每个JavaScript工程师都应懂的33个概念

    摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...

  9. 每个 JavaScript 工程师都应懂的33个概念

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...

随机推荐

  1. js数组的几个练习题

    第一次在博客园写文章,之前一直自己做记录.现在前端工作两年了,对前端整体技术有较清晰的了解.项目用了vue,react之类的写,如今打算从基础开始,慢慢深入了解原生的JS.这几天清明节,玩的嗨皮,最后 ...

  2. 关于laravel框架的跨域请求/jsonp请求的理解

    最近刚接触laravel框架,首先要写一个跨域的单点登录.被跨域的问题卡了两三天,主要是因为对跨域这快不了解,就在刚才有点茅塞顿开的感觉,我做一下大概整理,主要给一些刚接触摸不着头脑的看,哪里写得不对 ...

  3. Java基础学习(六)—List

    一.List 1.List集合特有功能 /* * List集合的特有功能: * A:添加功能 * void add(int index,Object element):在指定位置添加元素 * B:获取 ...

  4. Hibernate基础学习(五)—对象-关系映射(下)

    一.单向n-1 单向n-1关联只需从n的一端可以访问1的一端. 域模型: 从Order到Customer的多对一单向关联.Order类中定义一个Customer属性,而在Customer类不用存放Or ...

  5. Spring Ioc介绍和Bean的实例化

    一.IoC:Inverse of Control 控制反转   //  依赖注入  Dependency Injection 控制:某一接口具体实现类的选择权 反转:从调用者中移除控制权,转交第三方 ...

  6. Java Script 字符串操作

    JS中常用几种字符串操作: big() small() bold() fontcolor() fontsize() italics() strike() link() charAt() charCod ...

  7. 通过VMware安装Linux操作系统

    1.安装好vmvare,下载好Linux,本文采用redhat 6.5  (下载linux可参考http://www.linuxidc.com/Linux/2007-09/7399.htm) 2.选择 ...

  8. 一道CVTE前端二面笔试题

    题目:给你一个数组,输出数组中出现次数第n多的数字; 比如:[1,1,1,2,2,2,3,3,4,4,5,5,6,6,7]; 1---3次 2---3次 3---2次 4---2次 5---2次 6- ...

  9. C语言学习第四章

    今天学习C语言循环结构,为什么要用循环呢?因为有时候我们对一堆的数字进行重复的处理的时候要重复的编写一些相同或者差不多的代码,让程序显得很臃肿,而且写着也麻烦,如果用循环来写的话能简化很多,出错的话也 ...

  10. JS设计模式之---单例模式

    单例模式是保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式在现在面向对象的语言Java,C#,C++等等中也有很多用到,其实它在Javascript中使用同样非常广泛. var Cre ...