原型及原型链

原型基础概念

  1. function Person () {
  2. this.name = 'John';
  3. }
  4. var person = new Person();
  5. Person.prototype.say = function() {
  6. console.log('Hello,' + this.name);
  7. };
  8. person.say();//Hello,John

上述代码非常简单,Person原型对象定义了公共的say方法,虽然此举在构造实例之后出现,但因为原型方法在调用之前已经声明,当此实例本身没有此say方法时候,会在自身原型上查找到此方法。

原型链

  1. function Foo() {
  2. this.value = 42;
  3. }
  4. Foo.prototype = {
  5. method: function() {}
  6. };
  7. function Bar() {}
  8. // 设置Bar的prototype属性为Foo的实例对象
  9. Bar.prototype = new Foo();
  10. Bar.prototype.foo = 'Hello World';
  11. // 修正Bar.prototype.constructor为Bar本身
  12. Bar.prototype.constructor = Bar;
  13. var test = new Bar() // 创建Bar的一个新实例
  14. // 原型链
  15. test [Bar的实例]
  16. Bar.prototype [Foo的实例]
  17. { foo: 'Hello World' }
  18. Foo.prototype
  19. {method: ...};
  20. Object.prototype
  21. {toString: ... /* etc. */};

上面的例子中,test 对象从 Bar.prototype 继承下来;因此,它能访问 Bar的原型方法,同时Bar.prototypeFoo为Foo的实例对象,能够访问Foo的原型方法 method。它也能够访问Foo 实例属性 value。需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性。

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined,我们来看一个例子:


  1. function foo() {
  2. this.add = function (x, y) {
  3. return x + y;
  4. }
  5. }
  6. foo.prototype.add = function (x, y) {
  7. return x + y + 10;
  8. }
  9. Object.prototype.subtract = function (x, y) {
  10. return x - y;
  11. }
  12. var f = new foo();
  13. alert(f.add(1, 2)); //结果是3,而不是13
  14. alert(f.subtract(1, 2)); //结果是-1

通过代码运行,我们发现subtract是安装我们所说的向上查找来得到结果的,但是add方式有点小不同,这也是我想强调的,就是属性在查找的时候是先查找自身的属性,如果没有再查找原型,再没有,再往上走,一直插到Object的原型上,所以在某种层面上说,用 for in语句遍历属性的时候,效率也是个问题。

还有一点我们需要注意的是,js中基础构造器的prototype是不可改写的, 不可删除, 不可见的;

  1. Object.getOwnPropertyDescriptor(Number, 'prototype');
  2. // Object {value: Number, writable: false, enumerable: false, configurable: false};

hasOwnProperty函数:

hasOwnProperty是Object.prototype的一个方法,它可是个好东西,他能判断一个对象是否包含自定义属性而不是原型链上的属性,

  1. // 修改Object.prototype
  2. Object.prototype.bar = 1;
  3. var foo = {goo: undefined};
  4. foo.bar; // 1
  5. 'bar' in foo; // true
  6. foo.hasOwnProperty('bar'); // false
  7. foo.hasOwnProperty('goo'); // true

使用 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。

对象在查找属性时, 首先从自身查找, 查不到在原型链上查找, 层层向上一旦查到就返回, 直到查到 Object.protype 还查不到就返回undefined。

大家可以体会一下下面的结果, 建议动手画一下js中几种构造器和函数类型的原型链, 彻底理解他们之间的关系。

  1. Function.toString === Object.toString // true
  2. Function.prototype.toString === Object.toString // true
  3. Function.prototype.__proto__ === Object.prototype // ture
  4. Function.prototype.toString === Object.prototype.toString // false

当检查对象上某个属性是否存在时,hasOwnProperty 比较推荐的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子:

  1. // 修改 Object.prototype
  2. Object.prototype.bar = 1;
  3. var foo = {moo: 2};
  4. for(var i in foo) {
  5. console.log(i); // 输出两个属性:bar 和 moo
  6. }

我们没办法改变 for in 语句的行为,所以想过滤结果可以使用 hasOwnProperty 方法,代码如下:

  1. // foo 变量是上例中的
  2. for(var i in foo) {
  3. if (foo.hasOwnProperty(i)) {
  4. console.log(i);
  5. }
  6. }

这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次只输出 moo。如果不使用 hasOwnProperty,

也可以使用Object.key()来获取目标的属性和方法列表,得到的将是一个数组, 里面的属性是目标对象上的, 不含其原型上和其自身不可枚举的属性, 若要想得到更细致的结果可以使用 Object.getOwnPropertyNames() 配合 hasOwnProperty() 使用。

总结:推荐使用 hasOwnProperty,不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。

总结

原型极大地丰富了我们的开发代码,但是在平时使用的过程中请一定要注意上述提到的一些注意事项。

  1. 对象属性的查找规则, 原型链上属性之间的屏蔽。
  2. 深入理解 hasOwnProperty()for in 机制、Object.keys()
  3. in 操作符具有遍历到原型链顶端的特性,还能够从对象和原型链上不可枚举的属性拿到 true, 以至于我们 for in 遍历的时候要注意原型链上的方法。
  4. Object.getOwnPropertyDescriptor() 可以帮助我们更细致的了解对象上的属性。
  5. 也涉及到了 Object.defineProperty() 方法, 可用来非常细致的定义对象上的某个属性, 接受三个参数, 对象(object), 属性名(string), 属性描述器(object), 另外 Object.defineProperties() 也是相似的,只是它接受2个参数, 要被定义属性的对象(object), 属性描述集合props(object), 该方法可以一次定义多个属性。

Javascript原型介绍的更多相关文章

  1. 深入理解javascript原型和闭包(5)——instanceof

    又介绍一个老朋友——instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

  2. 深入理解javascript原型和闭包(8)——简述【执行上下文】上

    什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常.第二句.第三句输出都是undefined,说明浏览器在执行console.log(a)时, ...

  3. 深入理解javascript原型和闭包(10)——this

    接着上一节讲的话,应该轮到“执行上下文栈”了,但是这里不得不插入一节,把this说一下.因为this很重要,js的面试题如果不出几个与this有关的,那出题者都不合格. 其实,this的取值,分四种情 ...

  4. 深入理解javascript原型和闭包(11)——执行上下文栈

    继续上文的内容. 执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会产生执行上下文环境.当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境.处于活动状态的执行 ...

  5. 深入理解javascript原型和闭包(13)-【作用域】和【上下文环境】

    上文简单介绍了作用域,本文把作用域和上下文环境结合起来说一下,会理解的更深一些. 如上图,我们在上文中已经介绍了,除了全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了.而不 ...

  6. 深入理解javascript原型和闭包(15)——闭包

    前面提到的上下文环境和作用域的知识,除了了解这些知识之外,还是理解闭包的基础. 至于“闭包”这个词的概念的文字描述,确实不好解释,我看过很多遍,但是现在还是记不住. 但是你只需要知道应用的两种情况即可 ...

  7. 深入理解:JavaScript原型与继承

    深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解. 首先JavaScr ...

  8. javascript原型Prototype【转】

    转自:http://www.cnblogs.com/starof/p/4190404.html 在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同 ...

  9. javascript原型Prototype

    在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的 ...

随机推荐

  1. 洛谷P1105 普及- 生日

    标签:模拟,字符串,排序(快排) 这道题可以巧妙地运用结构体中记录在数组中的位置,来对sort做点手脚 题意本身就是记录一些人,他们出生日的日期,然后输出从小到大的名字.如果是同一天,则输出在输入序列 ...

  2. mysql COUNT()函数 语法

    mysql COUNT()函数 语法 作用:返回匹配指定条件的行数.博智达直线电机平台 语法:SELECT COUNT(*) FROM table_name mysql COUNT()函数 示例 // ...

  3. BZOJ 4769: 超级贞鱼 逆序对 + 归并排序

    手画几下序列的变换后发现逆序对数是恒定的,故只需对第 $0$ 年求逆序对即可. 树状数组会 $TLE$ 的很惨,需要用到归并排序来求逆序对. 其实就是省掉了一个离散化的时间,估计能比树状数组快一半的时 ...

  4. ModelSerializer 使用知识点_serializer.save(project=obj) #外键一定要作为实例传入save函数,否则无法新增成功

    1.有两个模型如下 A.project class Project(models.Model): """ 项目表 """ id = mode ...

  5. 消息 15135,级别 16,状态 8,过程 sp_addextendedproperty,对象无效。不允许有扩展属性,或对象不存在。

    不知道网上为啥有那么多复制粘贴的文章,写点原创不好吗?堂而皇之的贴别人的文章有意思吗? 消息 15135,级别 16,状态 8,过程 sp_addextendedproperty,对象无效.不允许有扩 ...

  6. windows与ubuntu双系统的安装

    将ubuntu镜像烧录至U盘,从U盘启动电脑 选择自定义安装,不要选择它本身的双系统选项. 我的方案分区: 1. 挂载点/:主分区:安装系统和软件:大小为30G:分区格式为ext4:2. 挂载点/ho ...

  7. linux 设置 hugepage

    临时设置 hugepage > /sys/kernel/mm/hugepages/hugepages-16384kB/nr_hugepages 查看是否设置成功 cat /proc/meminf ...

  8. leetcode-mid-sorting and searching - 56 Merge Intervals

    mycode 出现的问题:比如最后一个元素是[1,10],1小于前面所有元素的最小值,10大于前面所有元素的最大值,而我最开始的思路只考虑了相邻 参考: 思路:如果我只考虑相邻,必须先将list排序, ...

  9. MySql 使用递归函数时遇到的级联删除问题

    以下两段SQL的写法看似相同,结果效果却是不同的 写法A: DELETE OM_ORGANIZATION, OM_POSITION FROM OM_ORGANIZATION LEFT JOIN OM_ ...

  10. 八、RF的内置变量

    1.表示“空”的变量 ${EMPTY} 空 适用输入空的案例 2.表示“空格”的变量 ${SPACE} 空格,如果是需要5个空格可以这样写${SPACE*5} 3.目录的绝对路径 ${CURDIR} ...