JavaScript中的原型prototype完全解析_基础知识

   要理解JS中的prototype, 首先必须弄清楚以下几个概念
   1. JS中所有的东西都是对象

   2. JS中所有的东西都由Object衍生而来, 即所有东西原型链的终点指向Object.prototype
 

  // ["constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf",
   // "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__",
   // "__lookupSetter__"]
   console.log(Object.getOwnPropertyNames(Object.prototype));

   3. JS中构造函数和实例(对象)之间的微妙关系
   构造函数通过定义prototype来约定其实例的规格, 再通过 new 来构造出实例, 他们的作用就是生产对象.
   而构造函数(方法)本身又是方法(Function)的实例, 因此也可以查到它的__proto__(原型链)

   Object       / function F() {} 这样子的就是构造函数啦, 一个是JS原生API提供的, 一个是自定义的
   new Object() / new F()           这样子的就是实例啦
   实例就"只能"查看__proto__来得知自己是基于什么prototype被制造出来的,
   而"不能"再重新定义实例的prototype妄想创造出实例的实例了.

   实践出真知, 只有自己动手观察/思考才能真正领悟:

  // 先来看看构造函数到底是什么
  // function Empty() {}  function Empty() {}
  console.log(Function.prototype, Function.__proto__);
  // Object {}          function Empty() {}
  console.log(Object.prototype, Object.__proto__);
  function F() {}
  // F {}              function Empty() {}
  console.log(F.prototype, F.__proto__);

   你可能已经晕了, 我们来分解一下。

prototype
   prototype输出的格式为: 构造函数名 原型
   首先看下Object.prototype输出了什么?
   Object {} -> 前面的Object为构造函数的名称, 后面的那个表示原型, 这里是一个{}, 即一个Object对象的实例(空对象)
   那么 F {} 我们就明白是什么意思了, F 就是构造函数的名称, 原型也是一个空对象

  // 再来看看由构造函数构造出来的实例
  var o = new Object(); // var o = {};
  // undefined       Object {}
  console.log(o.prototype, o.__proto__);
  function F() {}
  var i = new F();
  // undefined       F {}
  console.log(i.prototype, i.__proto__);

   我们再深入一点, 定义下 F 的原型看看到底会发生些什么?

  function F() {}
  F.prototype.a = function() {};
  var i = new F();
  // undefined       F {a: function}
  console.log(i.prototype, i.__proto__);

   这样我们就清楚的看到 i 是由 F 构造出来的, 原型是 {a: function}, 就是原本的空对象原型新增了一个 a 方法

   我们再换一种情况, 完全覆盖 F 的原型会怎么样?
   

function F() {}
  F.prototype = {
    a: function() {}
  };
  var i = new F();
  // undefined       Object {a: function}
  console.log(i.prototype, i.__proto__);

   咦~ 为什么这里表明 i 是由 Object 构造出来的? 不对吧!
   因为我们完全将 F 的prototype覆盖, 其实也就是将原型指定为对象{a: function}, 但这会造成原本的constructor信息丢失, 变成了对象{a: function}指定的constructor.
   那么对象{a: function}的constructor是什么呢?
   因为对象{a: function}其实就相对于

  var o = {a: function() {}} // new了一个Object

   那么o的constructor当然是 Object 啦

   我们来纠正下这个错误

  function F() {}
  F.prototype = {
    a: function() {}
  }
  // 重新指定正确的构造函数
  F.prototype.constructor = F;
  var i = new F();
  // undefined       F {a: function, constructor: function}
  console.log(i.prototype, i.__proto__);

   现在又能得到正确的原型信息了~

原型链

   然后来看看什么原型链又是个什么东西?
   简单的来讲和OOP中的继承关系(链)是一样的, 一层一层往上找, 直至最终的 Object.prototype

      最最关键的是要弄清楚JS中哪些东西是(实例)对象, 这个简单了, JS中所有东西都是对象!
   再要弄清楚就是任何一个对象都是有一个原型的!

   那么我们来证明一下:
  

  Object // 这是一个函数, 函数是 Function 的实例对象, 那么就是由 Function 构造出来的
  Object.__proto__ == Function.prototype // 那么Object的原型, true
  // 这个是一个普通对象了, 因此属于 Object 的实例
  Function.prototype.__proto__ == Object.prototype // true
  // 这已经是原型链的最顶层了, 因此最终的指向 null
  Object.prototype.__proto__ == null // true

  Function // 这也是一个函数, 没错吧!
  Function.__proto__ == Function.prototype // true

  function A() {} // 这是一个自定义的函数, 终归还是一个函数, 没错吧!
  A.__proto__ == Function.prototype // 任何函数都是 Function 的实例, 因此A的原型是?
  var a = new A()
  a.__proto__ == A.prototype // 实例a是由A构造函数构造出来的, 因此a的原型是由A的prototype属性定义的
  A.prototype.__proto__ == Object.prototype // 普通对象都是 Object 的示例

Prototype和__proto__
每一个对象都包含一个__proto__,指向这个的对象的“原型”。
类似的事情是,每一个函数都包含一个prototype,这个prototype对象干什么的了?

咱们看看如下代码,用构造函数来创建一个对象(上面是用字面量的形式创建对象)。

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);

试想想,这个foo对象的__proto__会指向什么?

一个包含constructor属性的对象?看不太懂没关系,把函数Foo的prototype属性打印出来,对比一下就知道了。

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
console.log(Foo.prototype);
console.log(foo.__proto__ === Foo.prototype);

原来,new出来的对象foo的__proto__就只指向函数Foo的prototype。

foo.__proto__ --> Foo.prototype

JS这么设计有何意义了?回忆下上面说的,在JS的世界中,对象不是根据类(模具)创建出来的,而是从原型(另一个对象)衍生出来的。

当我们执行new操作创建一个新的对象时,先不深入new操作的具体实现,但有一点我们是肯定的——就是为新对象的__proto__指向一个原型对象。

就刚才这段代码

function Foo(){};
var foo = new Foo();

foo.__proto__到底要指向谁了?你怎么不能指向Foo这个函数本身吧,虽然函数也是对象,这个有机会会详细讲。但如何foo.__proto__指向Foo固然不合适,因为Foo是一个函数,有很多逻辑代码,foo作为一个对象,继承逻辑处理没有任何意义,它要继承的是“原型对象”的属性。

所以,每个函数会自动生成一个prototype对象,由这个函数new出来的对象的__proto__就指向这个函数的prototype。

foo.__proto__ --> Foo.prototype

总结
说了这么多,感觉还是没完全说清楚,不如上一张图。我曾经参考过其他网友的图,但总觉得哪里没说清楚,所以我自己画了一张图,如果觉得我的不错,请点个赞!(老子可是费了牛劲才画出来)。

咱们就着这张图,记住如下几个事实:

1. 每个对象中都有一个_proto_属性。

JS世界中没有类(模具)的概念,对象是从另一个对象(原型)衍生出来的,所以每个对象中会有一个_proto_属性指向它的原型对象。(参考左上角的那个用字面量形式定义的对象obj,它在内存中开辟了一个空间存放对象自身的属性,同时生成一个_proto_指向它的原型——顶层原型对象。)

2. 每个函数都有一个prototype属性。

“构造函数”为何叫构造函数,因为它要构造对象。那么根据上面第一条事实,构造出来的新对象的_proto_属性指向谁了?总不能指向构造函数自身,虽然它也是个对象,但你不希望新对象继承函数的属性与方法吧。所以,在每个构造函数都会有一个prototype属性,指向一个对象作为这个构造函数构造出来的新对象的原型。

3. 函数也是对象。

每个函数都有一些通用的属性和方法,比如apply()/call()等。但这些通用的方法是如何继承的呢?函数又是怎么创建出来的呢?试想想,一切皆对象,包括函数也是对象,而且是通过构造函数构造出来的对象。那么根据上面第二条事实,每个函数也会有_proto_指向它的构造函数的prototype。而这个构造函数的函数就是Function,JS中的所有函数都是由Function构造出来的。函数的通用属性与方法就存放在Function.prototype这个原型对象上。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索javascript
, js
, prototype
原型
原型prototype、prototype原型链、prototype js原型、prototype原型模式、javascript prototype,以便于您获取更多的相关知识。

时间: 2024-09-13 12:53:59

JavaScript中的原型prototype完全解析_基础知识的相关文章

在javascript中创建对象的各种模式解析_基础知识

最近在看<javascript高级程序设计>(第二版) javascript中对象的创建 •工厂模式 •构造函数模式 •原型模式 •结合构造函数和原型模式 •原型动态模式 面向对象的语言大都有一个类的概念,通过类可以创建多个具有相同方法和属性的对象.虽然从技术上讲,javascript是一门面向对象的语言,但是javascript没有类的概念,一切都是对象.任意一个对象都是某种引用类型的实例,都是通过已有的引用类型创建:引用类型可以是原生的,也可以是自定义的.原生的引用类型有:Object.A

javascript 中__proto__和prototype详解_基础知识

__proto__是内部原型,prototype是构造器原型(构造器其实就是函数) 构造器的原型(prototype)是一个对象 那什么是构造器呢? 要想创建一个对象,首先要有一个对象构造器,就像php里面一样,要想创建一个对象,首先要有一个类 构造器的实质就是一个函数,下面的问题是:如何通过这个构造器来创建一个对象呢? 答案: new 构造器构造的是对象. 一.所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function) 复制代

javascript中的原型链深入理解_基础知识

要弄清楚原型链就要先弄清楚 function 类型,在javascript中没有类的概念,都是函数,所以它是一门函数式的编程语言.类有一个很重要的特性,就是它可以根据它的构造函数来创建以它为模板的对象.在javascript中,函数就有2个功能 第一. 作为一般函数调用 第二. 作为它原型对象的构造函数 也就new() 我们来看一个例子 复制代码 代码如下: function a(){ this.name = 'a'; } 当创建一个函数,它会发生什么呢? 第一.它会创建1个函数对象 也就是a

JavaScript中Boolean对象的属性解析_基础知识

constructor 属性 实例 返回通过 myvar 对象的原型创建的函数: var myvar = new Boolean(1); myvar.constructor; 结果输出: function Boolean() { [native code] } 定义和用法 constructor 属性返回对创建此对象的 Boolean 函数的引用. prototype 构造器为 Boolean 对象创建一个新的方法: Boolean.prototype.myColor=function() {

JavaScript中原型和原型链详解_基础知识

javascript中的每个对象都有一个内置的属性prototype,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用.意思是是prototype属性保存着对另一个JavaScript对象的引用,这个对象作为当前对象的父对象. 复制代码 代码如下: A.prototype = new B(); 理解prototype不应把它和继承混淆.A的prototype为B的一个实例,可以理解A将B中的方法和属性全部克隆了一遍.A能使用B的方法和属性.这里强调的是克隆而不是

深入解析JavaScript中的数字对象与字符串对象_基础知识

JavaScript Number 对象JavaScript 只有一种数字类型. 可以使用也可以不使用小数点来书写数字.JavaScript 数字 JavaScript 数字可以使用也可以不使用小数点来书写: 实例 var pi=3.14; // 使用小数点 var x=34; // 不使用小数点 极大或极小的数字可通过科学(指数)计数法来写: 实例 var y=123e5; // 12300000 var z=123e-5; // 0.00123 所有 JavaScript 数字均为 64 位

JavaScript中数组继承的简单示例_基础知识

在写一些库时经常会用到树结构的数据,而且一些树形结构的数据对从根到叶的路径获取需求非常高.比如一个站点的整个路由表就是一棵这样的树,它的「路径」实际上就是 URL 中的 path 部分.所以我好几次都用了丧心病狂的数组继承去实现,下面给大家分享下. 在 JavaScript 中,数组也属于 Object 的一种,它也可以继承.任何两个对象本身就可以有继承关系,数组也不例外.于是我们让一个树的任何一个节点都是数组,它只维护自己下标最大的那个元素的值.其它元素的值通过原型继承从祖先节点继承而来.这样

简单理解JavaScript中的封装与继承特性_基础知识

JavaScript中的封装封装简单地说就是让外界只能访问对象的共有变量和函数,隐藏细节和数据. js中有三种方法创建对象,分别为门户大开型.用命名规范区分私有变量.闭包创建真正的私有变量三种. 1.门户大开型,是实现对象的最基础的方法,所有方法与变量都是共有的外界可以访问. var Book = function(name){ if(this.check(name)){ console.log("error"); throw new Error("name null&quo

详解JavaScript中undefined与null的区别_基础知识

有点奇怪的是,JavaScript语言居然有两个表示"无"的值:undefined和null.这是为什么? 一.相似性在JavaScript中,将一个变量赋值为undefined或null,老实说,几乎没区别. 复制代码 代码如下: var a = undefined;var a = null; 上面代码中,a变量分别被赋值为undefined和null,这两种写法几乎等价.undefined和null在if语句中,都会被自动转为false,相等运算符甚至直接报告两者相等. 复制代码