JS-Object(2) 原型对象 ,prototype属性。
- 基础✅
- prototype(✅)
- JS中的继承
- 使用JSON数据
- 构建对象实战
基础
关键字"this"指向了当前代码运行时的对象( 原文:the current object the code is being written inside )
定义template, 创建实际对象 object instances
constructor function: JavaScript 用一种称为构建函数的特殊函数来定义对象和它们的特征。
当一个对象实例需要从类中创建出来时,类的构造函数就会运行来创建这个实例。
这种创建对象实例的过程称之为实例化-实例对象被类实例化
非经典的OOP:object-oriented programming
不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。(参见 Object prototypes),所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同。
传统写法:
必须写return xxxx
function createNewPerson(name) { var obj = {}; obj.name = name; obj.greeting = function() { alert('Hi! I\'m' + this.name + ".") }; return obj }
创建一个constructor function ⚠️,构造器名字用首字母大写的name.
function Person(name) { this.name = name; this.greeting = function() { alert('Hi! I\'m' + this.name + ".") }; }
然后用构造器函数来创建一个实例
var person1 = new Person('Bob')
类似:new Vue({}) ; 实例化一个Vue对象。
需要⚠️:
创建新实例时,如果方法(这里是greeting)定义在构造器的函数体中,每次实例化都要定义这个方法,这不明智 ideal。
因此,使用prototype来定义函数。让实例继承这个方法就好, 这是构造器定义的定义约定。
Person.prototype.greeting = function() { ... }
小结:
多个创建object instance的方法:
- 声明一个对象字面量 literal (传统的方法)
- 使用constructor function (标准的方法)
- 使用Object()构造器
-
var person1 = new Object(); 然后再添加特性或方法,或者直接再new Object({添加}): person1.name = 'Chris'; person1['age'] = 38; person1.greeting = function() { alert('Hi! I\'m ' + this.name + '.'); };
-
- 使用JS内建的create()方法:(用于继承)
-
之前以及定义了person1对象 使用create方法,继承了person1的特性和方法: (本质上是指定一个原型对象) var person2 = Object.create(person1); person2.name person2.greeting()
Prototype
JS常常被描述为基于原型的语言。
每个对象都包含一个原型对象(我的理解就是类)。对象以其原型对象为模版,从原型对象继承方法和特性。
一个对象的原型对象也可以包含一个原型对象,从中继承方法和特性,层层类推。这涉及到原型链。prototype chain. (我的理解类似Ruby中的类的继承,形成一个祖先链。)
这就解释了为什么一个对象拥有定义在其他对象中的属性和方法,
因为这个对象继承了它的原型对象中的属性或方法。
对象和类:传统和JavaScript
- 传统的OOP, 首先定义类,然后以此创建实例,类的属性和方法被复制到实例上(这和Ruby语法不一样)。
- javascript, 在对象实例和它的构造器之间建立一个链接:__proto__属性, 之后通过上朔原型链prototype chain,在构造器中找到这些属性和方法。
使用JavaScript中的prototype
所有JS的函数有一个特殊的属性, prototype。
实例的_proto_属性就是它的构造器的prototype。(instance._proto_等于Constructor.prototype)
function doSomething() {};
doSomething.prototype.foo = "bar";
// 实例化doSometing函数
var instance = new doSometing();
// 给实例添加属性
instance.prop = 'hello';
//实例的_proto_属性就是它的构造器的prototype。
doSomething.prototype =>
{foo: "bar", constructor: ƒ}
instance.__proto__ =>
{foo: "bar", constructor: ƒ}
类似Ruby,通过_prop_这个链接向上级构造函数(类)找属性或方法
访问实例instance一个属性foo(instance.foo),如果实例属性中没有找到,则找它的构造器doSomething的prototype对象中的属性:
- 如果找到,返回值。(这里的案例找到并返回“bar”) 如果没有,浏览器就会上溯,去doSomething的prototype对象中的_proto_中找, 即它的原型window.Object的propotype中去找。
- 如果还没找到,证明浏览器上所有声明了的_proto_上都不存在这个属性,返回undefined
⚠️,强调一下,方法和属性没有被copy,而是顺着prototype chain向上找,找到就引用。
function doSomething(){}
doSomething.prototype.foo = "barr";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
结果
"doSomeInstancing.prop: some value"
"doSomeInstancing.foo: barr"
"doSomething.prop: undefined"
"doSomething.foo: undefined"
"doSomething.prototype.prop: undefined"
"doSomething.prototype.foo: barr"
⚠️: obj._proto_ 方法,现在被Object.getPrototypeOf(obj)取代
小结:prototype对象:是继承成员被定义的地方。
- 只有在prototype中被定义的属性/方法,才能够通过_proto_传递下去,被继承。
- 那些非prototype属性/方法,不会继承。只能被构造器自身使用。
javascript实例复制他的构造函数对象中的prototype中的属性:
假设list是Person.prototype中的属性, 值是“apple”。person1是Person的实例:
- read: (我的理解reference)调用person1.list,得到"apple"。这是person1实例通过原型链_proto_向上访问得到‘apple’值。
- write: (我的理解copy)调用person1.list = 'orange',list属性被copy到person1中。此时list:“orange”成为person1对象中的属性/值。
constructor属性
实例对象会从原型中继承一个constructor属性:该属性指向了用于构造次实例对象的构造函数。
person1.constructor返回构造person1实例对象的构造函数对象。
person2.constructor =>
ƒ Person(name){
this.name = name;
this.greeting = function() {
alert(this.name + "!!!")
};
}
因为返回构造器,可以利用这点新建一个实例对象:
var person3 = new person1.constructor("Snow")
另外,使用person1.constructor.name返回一个构造器的名字
修改原型
给构造器函数的prototype增加的方法, 都可以在它的实例中使用,这代表了继承。
总结:
构造器函数的建立方法:
- 在函数体中定义属性
- 在prototype中定义方法
// 构造器及其属性定义 function Test(a,b,c,d) { // 属性定义 }; // 定义第一个方法 Test.prototype.x = function () { ... } // 定义第二个方法 Test.prototype.y = function () { ... } // 等等……