JS 原型

转载自【EC前端 - JavaScript原型

原型是JavaScript最重要的概念。同时也是初级开发者最忌惮的内容,原因在于网上很少有关于它的合理描述。

但事实上,原型很简单,你可以很轻松的掌握它的知识要点。

什么是原型

了解什么是原型之前,我们先看一个示例:

var obj = {};
obj.toString(); // "[object Object]"

上面的例子中,我们声明了一个空对象,并没有为它添加toString属性方法,但这个方法却可以被成功调用并输出。是不是觉得很神奇?

原因在于JavaScript的对象都有一个内置的[[Prototype]]私有属性,这个属性指向另一个对象,我们称这个对象为原对象的原型。当JS引擎访问objtoString属性时,首先会去obj对象查找,发现找不到,就沿着obj[prototype]属性去他的原型上查找。

通过Chrome开发者工具,我们可以看到这个obj原型的真面目:

图中我们可以看到,obj的原型对象已经定义了一个toString属性。所以,空对象也可以使用toString()这个属性方法。

为什么需要原型

原型意义在于实现属性的继承

想象一下:编写一个JS脚本,创建1000个数组实例。如果在每个数组实例中单独定义数组的操作方法,不仅代码冗余,还会内存资源极大的浪费。有了原型,我们只需要把数组的操作方法定义在数组的原型上即可,实现了属性的共享。

举个例子:

我们希望JS的数字类型能提供一个方法判断当前数字是否为奇数,没有原型的情况下,你只能这么做:

var num = new Number(99);
num.isOdd = function(){return this % 100 !== 0}; num.isOdd(); // true

但是这样是没有意义的,因为isOdd()方法定义在num变量上面,其他数字类型并不能使用它:

var num2 = 100;
num2.isOdd(); // num2.isOdd is not a function

有了原型概念以后,由于所有数字类型都指向了同一个原型,我们可以把isOdd方法定义在这个原型上,这样,所有数字类型就都能调用到这个方法了:

var num1 = 99, num2 = 100, num3 = 0;
Number.prototype.isOdd = function(){return this % 100 !== 0}; num1.isOdd(); // true
num2.isOdd(); // false
num3.isOdd(); // false
学习后面的内容,你将明白:Number.prototype指向数字类型的原型

原型链

原型并不是一个特别的存在,它也只是一个普通的对象而已。

换句话说,原型也可以拥有属于它的原型。如果把对象的[[prototype]]属性想象成链条,就形成了一条原型链

接下来,我们通过一个示例来看下JS引擎是如何通过原型链查找属性的:

现在创建三个对象,并通过Object.setPrototypeOf()方法将它们连结成一条原型链:

var son = {a: 1, b: 2},
parent = {b: 3, c: 4},
ancestor = {d: 5}; Object.setPrototypeOf(son, parent);
Object.setPrototypeOf(parent, ancestor); son.a // 1
son.b // 2
son.c // 4
son.d // 5

这三个对象形成将形成一条原型链,JS引擎将从左往右有序地查找目标属性:

如何设置和修改对象的原型

JavaScript分别通过 Object.setPrototypeOf()Object.getPrototypeOf() 两个方法来设置和获取对象的原型。

var parent = {type: 'parent'}, son = {type: 'son'};
Object.setPrototypeOf(son, parent);
Object.getPrototypeOf(son) === parent // true

内置对象实例的原型

JavaScript提供了一些内置对象(构造函数),比如Object, String, Array, Boolean等等,它们提供了prototype属性,指向实例的原型。因此,可以简单地通过instance.constructor.prototype来获取

var obj = {}, str = '', arr = [], bl = true;

Object.getPrototypeOf(obj) === obj.constructor.prototype // true
Object.getPrototypeOf(str) === str.constructor.prototype // true
Object.getPrototypeOf(arr) === arr.constructor.prototype // true
Object.getPrototypeOf(bl) === bl.constructor.prototype // true
如果你不理解constructor这个属性,可以阅读构造函数一节。

通过instance.constructor.prototype这种方式获取原型的方式并不是绝对可靠的。因为实例的constructor属性是可改变的(mutable)。一旦属性,instance.constructor.prototype便无法正确指向实例的原型。

var obj = new Object();
obj.constructor = Array;
Object.getPrototypeOf(obj) === obj.constructor.prototype // false

上面的例子中,我们随意修改了obj的constructor属性,然后obj.constructor.prototype便不再指向obj的原型了。

另外,对于自定义构造函数而言,其constructor也是可变的(内置构造函数的constructor被配置为不可改变)

综合来说,我们推荐使用 Object.getPrototypeOf() 方法获取实例原型。

什么是JavaScript原型的更多相关文章

  1. 浅谈系列之 javascript原型与对象

    在我学习与使用javascript三个月中,我一直对javascript的继承关系以及prototype理解不清,导致很多时候为什么这么用说不出个所以然来.截止到本周为止,通过之前的学习以及自己的再学 ...

  2. JavaScript原型OOP——你上车了吗?

    .title-bar { width: 80%; height: 35px; padding-left: 35px; color: white; line-height: 35px; font-siz ...

  3. 深入理解javascript原型和闭包 (转)

    该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和闭包,当然,肯定少不了原型链和作用域链.帮 ...

  4. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  5. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  6. 深入理解javascript原型和闭包(2)——函数和对象的关系

    上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; co ...

  7. 深入理解javascript原型和闭包(3)——prototype原型

    既typeof之后的另一位老朋友! prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名.如果它还是您的新朋友,我估计您也是javascript的新朋友. 在咱们的第一节(深入理解 ...

  8. 深入理解javascript原型和闭包(4)——隐式原型

    注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看<javascript高级程序设计(第三版)>第6章:面向对象的程序设计. 上节已经提到 ...

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

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

  10. 深入理解javascript原型和闭包(6)——继承

    为何用“继承”为标题,而不用“原型链”? 原型链如果解释清楚了很容易理解,不会与常用的java/C#产生混淆.而“继承”确实常用面向对象语言中最基本的概念,但是java中的继承与javascript中 ...

随机推荐

  1. python调用GDAL实现几何校正

    引自https://blog.csdn.net/qq_27045589/article/details/81062586 def main(): infile = "F:\\Temp_Dat ...

  2. 分布式 基本理论 CAP 2

    关于P P, 即 Partition字面意思是网络分区,其实 包括了 各种网络问题, 我们要把它理解 一个 广义的 分区问题. P 涉及到了 时间, 这么说吧, 出现了分区, 那就是节点之间 “长久的 ...

  3. Rabbitmq 安装后采坑

    一.接手项目 接手项目后,按别人说的先安装什么,后安装什么然后就可以用了,也不去看什么.先开始安装的是otp_win64_19.1工具包和rabbitmq-server-3.6.5服务端,在win10 ...

  4. c语言操作符总结

    分类: 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 其他操作符(下标引用.函数调用和结构成员) 一.算数操作符 1.算术操作符包括:+  ...

  5. Monkey测试简介

    1.Monkey测试简介monkey是安卓命令行工具,它向系统发送伪随机的用户事件,例如:按键的输入.触摸屏的输入.手势输入等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常.因此,mo ...

  6. leetcode top 100 题目汇总

    首先表达我对leetcode网站的感谢,与高校的OJ系统相比,leetcode上面的题目更贴近工作的需要,而且支持的语言广泛.对于一些比较困难的题目,可以从讨论区中学习别人的思路,这一点很方便. 经过 ...

  7. LevelDB源码分析-Bloom Filter

    Bloom Filter bloom filter是leveldb中用来在一个block中检测key是否存在的工具,主要在BloomFilterPolicy类中实现: class BloomFilte ...

  8. 《Orange‘s》Loader

    Loader 作用 引导扇区只有512个字节,能做的事情很少,局限性太大.所以需要一个程序,通过引导扇区加载入内存,然后将控制权交给它,这样就突破了512字节的限制.这个程序便是loader. 加载过 ...

  9. git命令行获取某分支代码

    参考https://www.cnblogs.com/nylcy/p/6569284.html git clone -b branch1 http://xxx.xx.xxx... git clone - ...

  10. (5)Linux权限管理

    1.文件权限 2.1)文件类型 d:目录    -:文件    l:链接文件    b:可以存储的接口设备    c:串行端口设备(键盘,鼠标) 2)文件属性 接下来的九个字符以三个为一组分别是 rw ...