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 相关文章推荐
33种Javascript 表格排序控件收集
Dec 03 Javascript
jquery 注意事项与常用语法小结
Jun 07 Javascript
jquer之ajaxQueue简单实现代码
Sep 15 Javascript
把jquery 的dialog和ztree结合实现步骤
Aug 02 Javascript
如何动态的导入js文件具体该怎么实现
Jan 14 Javascript
jQuery实现伸展与合拢panel的方法
Apr 30 Javascript
bootstrap modal弹出框的垂直居中
Dec 14 Javascript
旺旺在线客服代码 旺旺客服代码生成器
Jan 09 Javascript
spirngmvc js传递复杂json参数到controller的实例
Mar 29 Javascript
Vue 组件注册实例详解
Feb 23 Javascript
使用Angular9和TypeScript开发RPG游戏的方法
Mar 25 Javascript
JavaScript封装单向链表的示例代码
Sep 17 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
给apache2.2加上mod_encoding模块後 php5.2.0 处理url出现bug
2007/04/12 PHP
删除html标签得到纯文本可处理嵌套的标签
2014/04/28 PHP
PHP彩蛋信息介绍和阻止泄漏的方法(隐藏功能)
2014/08/06 PHP
thinkPHP删除前弹出确认框的简单实现方法
2016/05/16 PHP
php使用FFmpeg接口获取视频的播放时长、码率、缩略图以及创建时间
2016/11/07 PHP
读jQuery之八 包装事件对象
2011/06/21 Javascript
用JavaScript仿PS里的羽化效果代码
2011/12/20 Javascript
fancybox modal的完美解决(右上的X)
2012/10/30 Javascript
js opener的使用详解
2014/01/11 Javascript
微信JS接口大全
2016/08/25 Javascript
js日期相关函数dateAdd,dateDiff,dateFormat等介绍
2016/09/24 Javascript
详解webpack中的hash、chunkhash、contenthash区别
2018/01/05 Javascript
vue基于mint-ui实现城市选择三级联动
2020/06/30 Javascript
详解webpack 入门与解析
2018/04/09 Javascript
JavaScript数组方法的错误使用例子
2018/09/13 Javascript
详解React 条件渲染
2020/07/08 Javascript
基于JavaScript实现简单扫雷游戏
2021/01/02 Javascript
js用正则表达式筛选年月日的实例方法
2021/01/04 Javascript
[05:34]2014DOTA2国际邀请赛中国区预选赛精彩TOPPLAY第二弹
2014/06/25 DOTA
Python遍历目录中的所有文件的方法
2016/07/08 Python
Python编程scoketServer实现多线程同步实例代码
2018/01/29 Python
Python2.7环境Flask框架安装简明教程【已测试】
2018/07/13 Python
python Flask 装饰器顺序问题解决
2018/08/08 Python
[机器视觉]使用python自动识别验证码详解
2019/05/16 Python
python实现简单的购物程序代码实例
2020/03/03 Python
python 中的命名空间,你真的了解吗?
2020/08/19 Python
HTML5自定义mp3播放器源码
2020/01/06 HTML / CSS
Deux par Deux官方网站:设计师童装
2020/01/03 全球购物
数据库什么时候应该被重组
2012/11/02 面试题
Internet主要有哪些网络群组成
2015/12/24 面试题
this关键字的含义
2015/04/08 面试题
建筑专业毕业生推荐信
2013/11/21 职场文书
槐乡的孩子教学反思
2014/04/27 职场文书
关于拾金不昧的感谢信
2015/01/21 职场文书
《黄道婆》教学反思
2016/02/22 职场文书
Python爬虫实战之爬取京东商品数据并实实现数据可视化
2021/06/07 Python