原型和属性的区别
前置:来自 <https://zhuanlan.zhihu.com/p/35458229>
JavaScript在设计之初,作为一种网页脚本语言,没有设计得很复杂,这种语言只要能够完成一些简单操作就够了。Javascript里面所有的数据类型都是对象(object)。在ES6之前,js中是没有Class的概念的(ES6中的类也是语法糖,本质还是基于原型),为了实现实例对象的属性和方法共享,就给function设计了一个prototype的概念。每个对象都有一个原型属性,原型属性指向另一个对象,而原型属性中的对象是会被其他对象所继承的,也就是共用的,对象自身的有些属性和方法则不可以。原型对象(prototype)也有一个自己的原型对象(_proto_),普通属性并非为对象。
关于jQuery框架
在前端入门时必须掌握的一个框架就是jQuery,其实你每次调用$(“…”)时,都会返回一个实例化的新的jQuery对象出来(内部帮你执行了new方法,关于jQuery初始化这一段也是jQuery的精髓之一,实现的相当巧妙,有兴趣可以去看看),这样做既没有使实例对象私有属性相互影响(如上面的propA),又能共用方法(如上面的methodB)。
原型链
下图通过原型链从而找到toString方法,而其他对象同样具有此方法,所以并不是对象会把这些方法和属性复制过来,只是通过原型(prototype)找到被继承的对象的原型。

理解对象的原型(prototype)(可以通过Object.getPrototypeOf(obj)或者已被弃用的proto属性获得)与构造函数的prototype属性之间的区别是很重要的。前者是每个实例上都有的属性,后者是构造函数的属性。也就是说,Object.getPrototypeOf(new Foobar())和Foobar.prototype指向着同一个对象。
对象这些公用属性和方法定义在 Object 的构造器函数 (constructor functions) 之上的prototype属性上(意味着可以被继承),而非对象实例本身。
_proto_和prototype的区别
Object.prototype的proto属性是一个访问器属性(一个getter函数和一个setter函数),它公开访问它的对象的内部[[Prototype]](对象或null)。尽量别用proto属性,存在争议。
prototype 指向一块内存,这个内存里面有共用属性;
proto 指向同一块内存;
prototype 和 proto 的不同点在于prototype 一般是构造函数的属性,而 proto 是对象的属性。
难点在于……构造函数也是对象!
如果没有 prototype,那么共用属性就没有立足之地,
如果没有 proto,那么一个对象就不知道自己的共用属性有哪些。上面所说proto属性是一个访问器属性,通俗来说,prototype就是用它来查找有哪些对象是可继承,且继承了的。
- prototype存储共用的属性和方法,
- proto用来将对象与该对象的原型相连,
- constuctor是用来将原型对象指向关联的构造函数。
在问题/object有详细的介绍。
_proto_的搜索
在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
function doSomething(){}
doSomething.prototype.foo = "bar"; // 在原型上添加一个property(属性)
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // 在对象上添加property(属性)
console.log( doSomeInstancing );
console.log("doSomeInstancing.prop: " + doSomeInstancing.prop);//some value
console.log("doSomeInstancing.foo: " + doSomeInstancing.foo);//bar
console.log("doSomething.prop: " + doSomething.prop);//underfined
console.log("doSomething.foo: " + doSomething.foo);//underfined
console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);//underfined
console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);//bar{ //在底层代码里面是有点套娃操作
prop: "some value",
__proto__: {
foo: "bar",
constructor: function doSomething(),
__proto__: {
constructor: function Object(),
hasOwnProperty: function hasOwnProperty(),
isPrototypeOf: function isPrototypeOf(),
propertyIsEnumerable: function propertyIsEnumerable(),
toLocaleString: function toLocaleString(),
toString: function toString(),
valueOf: function valueOf()
}
}
}doSomeInstancing 的 __proto__ 属性就是doSomething.prototype
个人理解,prototype有点类似于指针。但在object上一层的prototype则有点虚拟头的意思。(些许偏差,因为指针指向是一个节点,而prototype不断向上查找其他对象的prototype,直至找到所需要的方法或属性,没有则null,每个对象都具有prototype,而prototype指向另一个对象)

原型的实践
create()
我们可以使用Object.create()的方法,创建原型对象,例如:
var person1=Object.create(person);以person为原型对象创建了一个person1对象,在控制台输入以下代码返回person:
person1._proto_constructor属性
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于该构造此实例对象的构造函数,,
也就是可以使用它找到构造函数
person1.constructor; //如果构造此对象的是person函数,返回person(),没有的话就返回object()使用这个属性返回构造器,构造器是一个函数,故可以通过圆括号调用;只需在前面添加 new 关键字,便能将此函数作为构造器使用。当没有原始构造器的引用时,就可以使用此种方式。
var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);修改原型
//Person是构造person1的函数
Person.prototype.farewell = function() {
alert(this.name.first + ' has left the building. Bye for now!');
}在控制台输入person1.farewell()会看到一条警告信息,其中还显示了构造器中定义的人名;这很有用。但更关键的是,整条继承链动态地更新了,任何由此构造器创建的对象实例都自动获得了这个方法.
证明了先前的原型链模型这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。
关于属性定义在prototype,由于定义起来并不灵活,this的指向需要特别注意,例如:

一种常见的定义模型
在构造器(函数体)中定义属性、在 prototype 属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }典型的例子:school plan app
Rarrot