js老生常谈之this,constructor ,prototype全面解析


Posted in Javascript onApril 05, 2016

前言

javascript中的this,constructor ,prototype,都是老生常谈的问题,深入理解他们的含义至关重要。在这里,我们再来复习一下吧,温故而知新!

this

this表示当前对象,如果在全局作用范围内使用this,则指代当前页面对象window; 如果在函数中使用this,则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。

先看一个在全局作用范围内使用this的例子:

console.log(this === window); // true
console.log(window.alert === this.alert); // true
console.log(this.parseInt("021", 10)); // 10

函数中的this是在运行时决定的,而不是函数定义时,如下:

// 定义一个全局函数
    function foo() {
      console.log(this.fruit);
    }
    // 定义一个全局变量,等价于window.fruit = "apple";
    var fruit = "apple";
    // 此时函数foo中this指向window对象
    // 这种调用方式和window.foo();是完全等价的
    foo(); // "apple"

    // 自定义一个对象,并将此对象的属性foo指向全局函数foo
    var pack = {
      fruit: "orange",
      foo: foo
    };
    // 此时函数foo中this指向window.pack对象
    pack.foo(); // "orange"

全局函数apply和call可以用来改变函数中this的指向,如下:

// 定义一个全局函数
    function foo() {
      console.log(this.fruit);
    }

    // 定义一个全局变量
    var fruit = "apple";
    // 自定义一个对象
    var pack = {
      fruit: "orange"
    };

    // 等价于window.foo();
    foo.apply(window); // "apple"
    // 此时foo中的this === pack
    foo.apply(pack);  // "orange"

注:apply和call两个函数的作用相同,唯一的区别是两个函数的参数定义不同。

因为在JavaScript中函数也是对象,所以我们可以看到如下有趣的例子:

// 定义一个全局函数
    function foo() {
      if (this === window) {
        console.log("this is window.");
      }
    }

    // 函数foo也是对象,所以可以定义foo的属性boo为一个函数
    foo.boo = function() {
      if (this === foo) {
        console.log("this is foo.");
      } else if (this === window) {
        console.log("this is window.");
      }
    };
    // 等价于window.foo();
    foo(); // this is window.

    // 可以看到函数中this的指向调用函数的对象
    foo.boo(); // this is foo.

    // 使用apply改变函数中this的指向
    foo.boo.apply(window); // this is window.

prototype

prototype本质上还是一个JavaScript对象。

并且每个函数都有一个默认的prototype属性。 如果这个函数被用在创建自定义对象的场景中,我们称这个函数为构造函数。 比如下面一个简单的场景:

// 构造函数
    function Person(name) {
      this.name = name;
    }
    // 定义Person的原型,原型中的属性可以被自定义对象引用
    Person.prototype = {
      getName: function() {
        return this.name;
      }
    }
    var hao= new Person("haorooms");
    console.log(hao.getName());  // "haorooms"

作为类比,我们考虑下JavaScript中的数据类型 - 字符串(String)、数字(Number)、数组(Array)、对象(Object)、日期(Date)等。

我们有理由相信,在JavaScript内部这些类型都是作为构造函数来实现的,比如:

// 定义数组的构造函数,作为JavaScript的一种预定义类型
    function Array() {
      // ...
    }

    // 初始化数组的实例
    var arr1 = new Array(1, 56, 34, 12);
    // 但是,我们更倾向于如下的语法定义:
    var arr2 = [1, 56, 34, 12];

同时对数组操作的很多方法(比如concat、join、push)应该也是在prototype属性中定义的。 实际上,JavaScript所有的固有数据类型都具有只读的prototype属性 (这是可以理解的:因为如果修改了这些类型的prototype属性,则哪些预定义的方法就消失了), 但是我们可以向其中添加自己的扩展方法。

// 向JavaScript固有类型Array扩展一个获取最小值的方法
    Array.prototype.min = function() {
      var min = this[0];
      for (var i = 1; i < this.length; i++) {
        if (this[i] < min) {
          min = this[i];
        }
      }
      return min;
    };

    // 在任意Array的实例上调用min方法
    console.log([1, 56, 34, 12].min()); // 1

注意:这里有一个陷阱,向Array的原型中添加扩展方法后,当使用for-in循环数组时,这个扩展方法也会被循环出来。 下面的代码说明这一点(假设已经向Array的原型中扩展了min方法):

var arr = [1, 56, 34, 12];
    var total = 0;
    for (var i in arr) {
      total += parseInt(arr[i], 10);
    }
    console.log(total);  // NaN

解决方法也很简单:

var arr = [1, 56, 34, 12];
    var total = 0;
    for (var i in arr) {
      if (arr.hasOwnProperty(i)) {
        total += parseInt(arr[i], 10);
      }
    }
    console.log(total);  // 103

constructor

constructor始终指向创建当前对象的构造函数。比如下面例子:

// 等价于 var foo = new Array(1, 56, 34, 12);
    var arr = [1, 56, 34, 12];
    console.log(arr.constructor === Array); // true
    // 等价于 var foo = new Function();
    var Foo = function() { };
    console.log(Foo.constructor === Function); // true
    // 由构造函数实例化一个obj对象
    var obj = new Foo();
    console.log(obj.constructor === Foo); // true

    // 将上面两段代码合起来,就得到下面的结论
    console.log(obj.constructor.constructor === Function); // true

但是当constructor遇到prototype时,有趣的事情就发生了。 我们知道每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:

function Person(name) {
      this.name = name;
    };
    Person.prototype.getName = function() {
      return this.name;
    };
    var p = new Person("haorooms");

    console.log(p.constructor === Person); // true
    console.log(Person.prototype.constructor === Person); // true
    // 将上两行代码合并就得到如下结果
    console.log(p.constructor.prototype.constructor === Person); // true

当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖), constructor的行为就有点奇怪了,如下示例:

function Person(name) {
      this.name = name;
    };
    Person.prototype = {
      getName: function() {
        return this.name;
      }
    };
    var p = new Person("haorooms");
    console.log(p.constructor === Person); // false
    console.log(Person.prototype.constructor === Person); // false
    console.log(p.constructor.prototype.constructor === Person); // false

为什么呢? 原来是因为覆盖Person.prototype时,等价于进行如下代码操作:

Person.prototype = new Object({
      getName: function() {
        return this.name;
      }
    });

而constructor始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:

function Person(name) {
      this.name = name;
    };
    Person.prototype = {
      getName: function() {
        return this.name;
      }
    };
    var p = new Person("haorooms");
    console.log(p.constructor === Object); // true
    console.log(Person.prototype.constructor === Object); // true
    console.log(p.constructor.prototype.constructor === Object); // true

怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:

function Person(name) {
      this.name = name;
    };
    Person.prototype = {
      getName: function() {
        return this.name;
      }
    };
    Person.prototype.constructor = Person;
    var p = new Person("haorooms");
    console.log(p.constructor === Person); // true
    console.log(Person.prototype.constructor === Person); // true
    console.log(p.constructor.prototype.constructor === Person); // true

也可以这么写:

function Person(name) {
      this.name = name;
    };
    Person.prototype = {
     constructor:Person,//指定constructor
      getName: function() {
        return this.name;
      }
    };

以上这篇js老生常谈之this,constructor ,prototype全面解析就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery Ajax 全解析
Feb 08 Javascript
js 返回时间戳所对应的具体时间
Jul 20 Javascript
javascript中call和apply方法浅谈
Sep 27 Javascript
一个不错的字符串转码解码函数(自写)
Jul 31 Javascript
jquery datatable后台封装数据示例代码
Aug 07 Javascript
面向切面编程(AOP)的理解
May 01 Javascript
javascript实现可拖动变色并关闭层窗口实例
May 15 Javascript
jquery实现页面加载效果
Feb 21 Javascript
vue2.0 常用的 UI 库实例讲解
Dec 12 Javascript
Vue组件库发布到npm详解
Feb 17 Javascript
JS中this的指向以及call、apply的作用
May 06 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
Sep 06 Javascript
实例详解ECMAScript5中新增的Array方法
Apr 05 #Javascript
关于JS中的apply,call,bind的深入解析
Apr 05 #Javascript
javascript中apply、call和bind的使用区别
Apr 05 #Javascript
JavaScript数组去重的两种方法推荐
Apr 05 #Javascript
jquery Deferred 快速解决异步回调的问题
Apr 05 #Javascript
JS模拟简易滚动条效果代码(附demo源码)
Apr 05 #Javascript
JS中的forEach、$.each、map方法推荐
Apr 05 #Javascript
You might like
PHP COOKIE设置为浏览器进程
2009/06/21 PHP
PHP新手用的Insert和Update语句构造类
2012/03/31 PHP
用PHP即时捕捉PHP中的错误并发送email通知的实现代码
2013/01/19 PHP
非常实用的php弹出错误警告函数扩展性强
2014/01/17 PHP
PHP实现接收二进制流转换成图片的方法
2017/01/10 PHP
修改发贴的编辑功能
2007/03/07 Javascript
JScript分割字符串示例代码
2013/09/04 Javascript
通过action传过来的值在option获取进行验证的方法
2013/11/14 Javascript
jQuery选择器全面总结
2014/01/06 Javascript
JavaScript中利用Array和Object实现Map的方法
2015/07/27 Javascript
javascript中利用柯里化函数实现bind方法
2016/04/29 Javascript
js实现页面刷新滚动条位置不变
2016/11/27 Javascript
详谈jQuery.load()和Jsp的include的区别
2017/04/12 jQuery
详解EasyUi控件中的Datagrid
2017/08/23 Javascript
Vue入门之数量加减运算操作示例
2018/12/11 Javascript
微信小程序组件传值图示过程详解
2019/07/31 Javascript
Vue+Java 通过websocket实现服务器与客户端双向通信操作
2020/09/22 Javascript
[03:06]V社市场总监Dota2项目负责人Erik专访:希望更多中国玩家加入DOTA2
2014/07/11 DOTA
Python素数检测的方法
2015/05/11 Python
Python Socket编程详细介绍
2017/03/23 Python
python urllib urlopen()对象方法/代理的补充说明
2017/06/29 Python
Pycharm技巧之代码跳转该如何回退
2017/07/16 Python
[原创]教女朋友学Python3(二)简单的输入输出及内置函数查看
2017/11/30 Python
Python用imghdr模块识别图片格式实例解析
2018/01/11 Python
通过Py2exe将自己的python程序打包成.exe/.app的方法
2018/05/26 Python
python批量修改文件夹及其子文件夹下的文件内容
2019/03/15 Python
Python3进制之间的转换代码实例
2019/08/24 Python
CSS3 文字动画效果
2020/11/12 HTML / CSS
婚纱摄影师求职信范文
2014/04/17 职场文书
县人大领导班子四风对照检查材料思想汇报
2014/10/09 职场文书
推广普通话的宣传语
2015/07/13 职场文书
党员公开承诺书(2016最新版)
2016/03/24 职场文书
css3实现背景图片颜色修改的多种方式
2021/04/13 HTML / CSS
NodeJs内存占用过高的排查实战记录
2021/05/10 NodeJs
nginx刷新页面出现404解决方案(亲测有效)
2022/03/18 Servers
基于CSS制作创意端午节专属加载特效
2022/06/01 HTML / CSS