原型与原型链,对象的创建

时间:2019-09-24 01:21来源:关于计算机
前端基础进级(9):详解面向对象、构造函数、原型与原型链 2017/04/02 · JavaScript· 1 评论 ·原型,原型链,构造函数,面向对象 原稿出处: 波同学    . 假设要自己计算一下就学前端以来

前端基础进级(9):详解面向对象、构造函数、原型与原型链

2017/04/02 · JavaScript · 1 评论 · 原型, 原型链, 构造函数, 面向对象

原稿出处: 波同学   

图片 1

.

假设要自己计算一下就学前端以来笔者遇上了怎么着瓶颈,那么面向对象一定是首先个坚决想到的。纵然笔者今日对此面向对象有了部分的刺探,但是那时的这种似懂非懂的悲凉,依旧心向往之。

为了帮忙我们能够进一步直观的上学和询问面向对象,作者会用尽量简单易懂的描述来突显面向对象的连锁知识。並且也计划了部分实用的例证扶助大家尤为便捷的左右面向对象的真理。

  • jQuery的面向对象实现
  • 包裹拖拽
  • 简易版运动框架封装

那只怕会花一点时日,可是却值得期待。所以只要有意思味的意中人能够来简书和大伙儿号关心自身。

而那篇作品首要来聊一聊关于面向对象的部分至关心珍视要的基本功。

Object类型是JavaScript中选用最多的一种类型。成立Object实例的章程有多样,接下去一一列举。

一、对象的定义

在ECMAScript-262中,对象被定义为“冬辰属性的相会,其特性能够包括基本值,对象大概函数”

约等于说,在JavaScript中,对象只是便是由局地列冬天的key-value对构成。当中value能够是基本值,对象可能函数。

// 这里的person正是贰个目的 var person = { name: 'Tom', age: 18, getName: function() {}, parent: {} }

1
2
3
4
5
6
7
// 这里的person就是一个对象
var person = {
    name: 'Tom',
    age: 18,
    getName: function() {},
    parent: {}
}

创立对象

大家可以通过new的措施创设贰个指标。

var obj = new Object();

1
var obj = new Object();

也能够因此对象字面量的花样创造二个简短的指标。

var obj = {};

1
var obj = {};

当大家想要给大家创制的粗略对象增多方法时,能够如此表示。

// 能够如此 var person = {}; person.name = "TOM"; person.getName = function() { return this.name; } // 也得以这么 var person = { name: "TOM", getName: function() { return this.name; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 可以这样
var person = {};
person.name = "TOM";
person.getName = function() {
    return this.name;
}
 
// 也可以这样
var person = {
    name: "TOM",
    getName: function() {
        return this.name;
    }
}

拜见对象的性质和格局

假如大家有一个差非常的少的目标如下:

var person = { name: 'TOM', age: '20', getName: function() { return this.name } }

1
2
3
4
5
6
7
var person = {
    name: 'TOM',
    age: '20',
    getName: function() {
        return this.name
    }
}

当大家想要访谈他的name属性时,能够用如下两种方式访谈。

person.name // 或者 person['name']

1
2
3
4
person.name
 
// 或者
person['name']

只要大家想要访谈的属性名是三个变量时,平常会使用第三种方法。举例大家要同一时候做客person的name与age,能够如此写:

['name', 'age'].forEach(function(item) { console.log(person[item]); })

1
2
3
['name', 'age'].forEach(function(item) {
    console.log(person[item]);
})

这种方法必然要敬爱,记住它今后在大家管理错综复杂数据的时候会有极大的援救。

1. Object构造函数

var person = new Object();
person.name = "Brittany";
person.age = 23;
person.job = "web front-end engineer";
person.sayName = function() {
    console.log(this.name);
};
person.sayName();   //Brittany

二、工厂方式

动用方面包车型地铁秘技创设对象很简短,不过在好些个时候并无法满意大家的要求。就以person对象为例。假若大家在实质上开垦中,不仅须要三个名字叫做TOM的person对象,相同的时候还索要别的四个名称为Jake的person对象,就算她们有广大相似之处,但是我们只可以再度写两回。

var perTom = { name: 'TOM', age: 20, getName: function() { return this.name } }; var perJake = { name: 'Jake', age: 22, getName: function() { return this.name } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var perTom = {
    name: 'TOM',
    age: 20,
    getName: function() {
        return this.name
    }
};
 
var perJake = {
    name: 'Jake',
    age: 22,
    getName: function() {
        return this.name
    }
}

很醒目那并不是有理的秘技,当相似对象太多时,大家都会崩溃掉。

我们得以动用工厂情势的情势消除这几个标题。看名称就能够想到其意义,工厂方式就是我们提供二个模子,然后通过那几个模型复制出大家须要的指标。大家供给多少个,就复制多少个。

var createPerson = function(name, age) { // 声美赞臣当中档对象,该对象正是工厂方式的模型 var o = new Object(); // 依次增加大家须要的属性与艺术 o.name = name; o.age = age; o.getName = function() { return this.name; } return o; } // 创造多个实例 var per汤姆= createPerson('TOM', 20); var PerJake = createPerson('Jake', 22);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var createPerson = function(name, age) {
 
    // 声明一个中间对象,该对象就是工厂模式的模子
    var o = new Object();
 
    // 依次添加我们需要的属性与方法
    o.name = name;
    o.age = age;
    o.getName = function() {
        return this.name;
    }
 
    return o;
}
 
// 创建两个实例
var perTom = createPerson('TOM', 20);
var PerJake = createPerson('Jake', 22);

深信不疑上边的代码并轻巧驾驭,也不用把工厂形式看得太过巨大上。很显眼,工厂模式帮衬大家解决了重新代码上的难为,让我们得以写比较少的代码,就能够成立相当多少个person对象。但是此间还应该有多个勤奋,须求我们注意。

首先个麻烦正是那样管理,我们并未主意鉴定分别对象实例的档期的顺序。使用instanceof能够识别对象的类别,如下例子:

var obj = {}; var foo = function() {} console.log(obj instanceof Object); // true console.log(foo instanceof Function); // true

1
2
3
4
5
var obj = {};
var foo = function() {}
 
console.log(obj instanceof Object);  // true
console.log(foo instanceof Function); // true

由此在工厂形式的根基上,大家要求动用构造函数的办法来缓慢解决这些麻烦。

2. 指标字面量情势

var person = {
    name: "Brittany",
    age: 23,
    job: "web front-end engineer",
    sayName: function() {
        console.log(this.name);
    }
};
person.sayName();

虽说Object构造函数或对象字面量都足以用来创制单个对象,但这一个点子有个鲜明的毛病:使用同叁个接口创造相当多指标,会发出大批量的重新代码。为消除那么些主题素材,能够行使工厂形式的一种变体。

三、构造函数

在JavaScript中,new关键字能够让三个函数变得十分。通过下边包车型地铁例证,大家来一探new关键字的美妙之处。

function demo() { console.log(this); } demo(); // window new demo(); // demo

1
2
3
4
5
6
function demo() {
    console.log(this);
}
 
demo();  // window
new demo();  // demo

为了能够直观的感想他们分化,建议我们动手实施观望一下。很刚毅,使用new之后,函数内部爆发了部分生成,让this指向改造。那么new关键字到底做了什么业务呢。嗯,其实本人以前在篇章里用文字大致说明了须臾间new到底干了哪些,不过有的同学好奇心很足,总希望用代码完毕一下,笔者就大约以本身的接头来抒发一下吗。

// 先一本正经的创设一个构造函数,其实该函数与平时函数并无异 var Person = function(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } // 将构造函数以参数情势传播 function New(func) { // 声澳优(Ausnutria Hyproca)在那之中档对象,该目的为尾声回到的实例 var res = {}; if (func.prototype !== null) { // 将实例的原型指向构造函数的原型 res.__proto__ = func.prototype; } // ret为构造函数实施的结果,这里经过apply,将构造函数内部的this指向修改为指向res,即为实例对象 var ret = func.apply(res, Array.prototype.slice.call(arguments, 1)); // 当我们在构造函数中显著钦赐了回来对象时,那么new的实践结果正是该再次来到对象 if ((typeof ret === "object" || typeof ret === "function") && ret !== null) { return ret; } // 若无分明性钦定重临对象,则默许重回res,这一个res正是实例对象 return res; } // 通过new注脚创设实例,这里的p1,实际收到的正是new中回到的res var p1 = New(Person, 'tom', 20); console.log(p1.getName()); // 当然,这里也足以判别出实例的品种了 console.log(p1 instanceof Person); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 先一本正经的创建一个构造函数,其实该函数与普通函数并无区别
var Person = function(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        return this.name;
    }
}
 
// 将构造函数以参数形式传入
function New(func) {
 
    // 声明一个中间对象,该对象为最终返回的实例
    var res = {};
    if (func.prototype !== null) {
 
        // 将实例的原型指向构造函数的原型
        res.__proto__ = func.prototype;
    }
 
    // ret为构造函数执行的结果,这里通过apply,将构造函数内部的this指向修改为指向res,即为实例对象
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
 
    // 当我们在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
 
    // 如果没有明确指定返回对象,则默认返回res,这个res就是实例对象
    return res;
}
 
// 通过new声明创建实例,这里的p1,实际接收的正是new中返回的res
var p1 = New(Person, 'tom', 20);
console.log(p1.getName());
 
// 当然,这里也可以判断出实例的类型了
console.log(p1 instanceof Person); // true

JavaScript内部再经过其他的部分奇异管理,将var p1 = New(Person, 'tom', 20); 等效于var p1 = new Person('tom', 20);。正是我们认知的new关键字了。具体怎么管理的,作者也不明白,别刨根问底了,一直回答下去太难 – -!

老实巴交讲,你大概很难在另外地点来看有这么斐然的告诉你new关键字到底对构造函数干了怎么样的篇章了。领会了这段代码,你对JavaScript的通晓又比旁人深入了一分,所以,一本正经无耻之尤求个赞可好?

当然,比非常多朋友由于对于日前几篇小说的学问精通远远不够到位,会对new的贯彻表示十二分纳闷。可是老实讲,即使您读了自己的先头几篇小说,一定会对这里new的完毕有似曾相识的以为。何况笔者那边曾经努力做了详细的评释,剩下的只可以靠你和谐了。

可是假设您花点时间,明白了他的原理,那么麻烦了广大人的构造函数中this到底指向何人就变得非常轻巧了。

进而,为了能够判明实例与对象的涉及,大家就选择构造函数来解决。

var Person = function(name, age) { this.name = name; this.age = age; this.getName = function() { return this.name; } } var p1 = new Person('Ness', 20); console.log(p1.getName()); // Ness console.log(p1 instanceof Person); // true

1
2
3
4
5
6
7
8
9
10
11
12
var Person = function(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        return this.name;
    }
}
 
var p1 = new Person('Ness', 20);
console.log(p1.getName());  // Ness
 
console.log(p1 instanceof Person); // true

至于构造函数,假诺你一时半刻不可能知道new的现实性达成,就先记住下边那多少个结论吧。

  • 与普通函数比较,构造函数并不曾别的特别的地方,首字母大写只是大家约定的小规定,用于区分普通函数;
  • new关键字让构造函数具备了与一般函数不一致的广大特点,而new的经过中,推行了如下进程:
    1. 声称二个在那之中对象;
    2. 将该中间对象的原型指向构造函数的原型;
    3. 将构造函数的this,指向该中间对象;
    4. 回来该中间对象,即再次回到实例对象。

3. 工厂格局 

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        console.log(this.name);
    };
    return o;
}

var person1 = createPerson("Brittany", 23, "Software Engineer");
var person2 = createPerson("Sam", 26, "Software Engineer");
console.log(typeof person1);   //Object

工厂格局就算缓和了创办八个一般对象的标题,但却未有化解对象识别的难点(即如何掌握叁个目的的连串)。如代码中只好检测出person1为Object类型。随着JavaScript的发展,又八个新形式出现了。

四、原型

固然构造函数化解了决断实例类型的难题,可是,提及底,依然八个指标的复制进程。跟工厂形式颇有相似之处。也正是说,当大家注脚了玖二十一个person对象,那么就有九二十个getName方法被重新生成。

那边的每三个getName方法完成的功力实在是一模二样的,可是由于个别属于区别的实例,就只可以直接不停的为getName分配空间。那就是工厂情势存在的第4个劳顿。

领悟那是不创造的。我们期待的是,既然都以兑现同二个功能,那么能否就让每二个实例对象都访谈同叁个方法?

当然能,这就是原型对象要帮大家缓慢解决的主题材料了。

我们创造的每贰个函数,都得以有二个prototype属性,该属性指向二个对象。这几个目的,就是我们那边说的原型。

当大家在成立对象时,可以依靠自个儿的急需,选取性的将部分性质和章程通过prototype属性,挂载在原型对象上。而每二个new出来的实例,都有二个__proto__属性,该属性指向构造函数的原型对象,通过那天个性,让实例对象也能够访谈原型对象上的法子。由此,当全部的实例都能够透过__proto__会见到原型对象时,原型对象的章程与本性就成为了共有方法与质量。

咱俩由此一个大致的事例与图示,来打听构造函数,实例与原型三者之间的关联。

鉴于每种函数都足以是构造函数,各类对象都能够是原型对象,由此一旦在知晓原型之初就想的太多太复杂的话,反而会阻拦你的敞亮,这里咱们要学会先简化它们。就独自的解析那三者的关系。

// 证明构造函数 function Person(name, age) { this.name = name; this.age = age; } // 通过prototye属性,将艺术挂载到原型对象上 Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); var p2 = new Person('jak', 22); console.log(p1.getName === p2.getName); // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 声明构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
// 通过prototye属性,将方法挂载到原型对象上
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
var p2 = new Person('jak', 22);
console.log(p1.getName === p2.getName); // true

图片 2

图示

由此图示大家得以观察,构造函数的prototype与具有实例对象的__proto__都指向原型对象。而原型对象的constructor指向构造函数。

除开,还足以从图中看出,实例对象实际对前方大家所说的中间对象的复制,而当中对象中的属性与艺术都在构造函数中增加。于是依据构造函数与原型的性状,大家就足以将要构造函数中,通过this注脚的习性与办法称为私有变量与办法,它们被当下被某多少个实例对象所独有。而由此原型评释的属性与艺术,我们得以称之为共有属性与格局,它们得以被全数的实例对象访问。

当大家拜见实例对象中的属性只怕措施时,会优先访问实例对象自己的习性和方法。

function Person(name, age) { this.name = name; this.age = age; this.getName = function() { console.log('this is constructor.'); } } Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); p1.getName(); // this is constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        console.log('this is constructor.');
    }
}
 
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
 
p1.getName(); // this is constructor.

在那几个事例中,大家同不经常候在原型与构造函数中都宣称了贰个getName函数,运营代码的结果表示原型中的访谈并未被访谈。

作者们仍可以透过in来决断,三个指标是或不是具有某多天性质/方法,无论是该属性/方法存在与实例对象照旧原型对象。

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function() { return this.name; } var p1 = new Person('tim', 10); console.log('name' in p1); // true

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.getName = function() {
    return this.name;
}
 
var p1 = new Person('tim', 10);
 
console.log('name' in p1); // true

in的这种特点最常用的情景之一,就是推断当前页面是不是在活动端张开。

isMobile = 'ontouchstart' in document; // 很三个人喜悦用浏览器UA的办法来剖断,但并非很好的点子

1
2
3
isMobile = 'ontouchstart' in document;
 
// 很多人喜欢用浏览器UA的方式来判断,但并不是很好的方式

更轻便的原型写法

据书上说前边例子的写法,假若我们要在原型上增加愈来愈多的不二等秘书诀,能够如此写:

function Person() {} Person.prototype.getName = function() {} Person.prototype.getAge = function() {} Person.prototype.sayHello = function() {} ... ...

1
2
3
4
5
6
function Person() {}
 
Person.prototype.getName = function() {}
Person.prototype.getAge = function() {}
Person.prototype.sayHello = function() {}
... ...

除此而外,作者还足以使用尤其轻松的写法。

function Person() {} Person.prototype = { constructor: Person, getName: function() {}, getAge: function() {}, sayHello: function() {} }

1
2
3
4
5
6
7
8
function Person() {}
 
Person.prototype = {
    constructor: Person,
    getName: function() {},
    getAge: function() {},
    sayHello: function() {}
}

这种字面量的写法看上去大约比相当多,不过有二个内需特别注意的地方。Person.prototype = {}骨子里是双重创制了四个{}对象并赋值给Person.prototype,这里的{}并非最先的不行原型对象。因而它当中并不富含constructor属性。为了确认保证科学,大家亟须在新制造的{}目的中显得的设置constructor的指向。即下面的constructor: Person

4. 构造函数情势

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        console.log(this.name);
    }    
}
var person1 = new Person("Brittany", 23, "Web front-end engineer");
var person2 = new Person("Closure", 26, "Manager");
person1.sayName();
person2.sayName();
console.log(person1.sayName == person2.sayName);   //false

使用构造函数的主要难题:各样方法都要在各类实例上再也成立一回。如代码中所示,person1的sayName和person2的sayName不等于。能够将函数定义转移到构造函数外界来消除。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}
function sayName() {
    console.log(this.name);
}

sayName函数的定义转移到了构造函数外界。而在构造函数内部,大家将sayName属性设置成等于全局的sayName函数。这样一来,由于sayName满含的是三个针对性函数的指针,因而person1和person2对象就共享了在全局功用域中定义的同叁个sayName()函数。那着实消除了四个函数做同样件事的难点,然而新主题材料又来了:在大局效用域中定义的函数实际上只可以被有个别对象调用,那让全局作用域有一些因陋就简。而更让令人无能为力经受的是:即使须求定义比比较多方法,那将在定义很四个全局函数,于是这些自定义的引用类型就无封装性可言。那些难题可通过使用原型方式来化解。

四、原型链

原型对象实际也是经常的目的。差不离全数的靶子都大概是原型对象,也恐怕是实例对象,何况还能够况且是原型对象与实例对象。这样的一个指标,便是结合原型链的一个节点。由此精通了原型,那么原型链并非一个多么繁杂的定义。

我们通晓全数的函数都有贰个称呼toString的办法。那么那么些办法到底是在哪个地方的啊?

先随机声喜宝(Hipp)个函数:

function foo() {}

1
function foo() {}

这就是说大家可以用如下的图来表示那么些函数的原型链。

图片 3

原型链

中间foo是Function对象的实例。而Function的原型对象同有时候又是Object的实例。那样就组成了一条原型链。原型链的看望,其实跟成效域链有非常的大的相似之处,他们都以二回单向的搜索进程。因此实例对象能够通过原型链,访谈到地处原型链上对象的持有属性与办法。那也是foo最后能够访问到地处Object原型对象上的toString方法的案由。

根据原型链的表征,大家得以很自在的兑现继承

5. 原型情势

五、继承

小编们平时结合构造函数与原型来创立一个目的。因为构造函数与原型的例外风味,分别化解了小编们不一样的搅扰。因此当大家想要完结持续时,就务须得依据构造函数与原型的不如而采用两样的宗旨。

笔者们声可瑞康(Karicare)(Karicare)个Person对象,该指标将用作父级,而子级cPerson将要继续Person的享有属性与措施。

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function() { return this.name; }

1
2
3
4
5
6
7
8
function Person(name, age) {
    this.name = name;
    this.age = age;
}
 
Person.prototype.getName = function() {
    return this.name;
}

率先我们来看构造函数的接续。在上面大家早就驾驭了构造函数的本来面目,它实质上是在new内部完结的一个复制进程。而我辈在继续时想要的,便是想父级构造函数中的操作在子级的构造函数中复发二回就能够。大家得以经过call方法来达到目标。

// 构造函数的接续 function cPerson(name, age, job) { Person.call(this, name, age); this.job = job; }

1
2
3
4
5
// 构造函数的继承
function cPerson(name, age, job) {
    Person.call(this, name, age);
    this.job = job;
}

而原型的存续,则只要求将子级的原型对象设置为父级的一个实例,加入到原型链中就可以。

// 承袭原型 cPerson.prototype = new Person(name, age); // 增加更加多措施 cPerson.prototype.getLive = function() {}

1
2
3
4
5
// 继承原型
cPerson.prototype = new Person(name, age);
 
// 添加更多方法
cPerson.prototype.getLive = function() {}

图片 4

原型链

理当如此关于继续还会有更加好的办法,这里就不做深入介绍了,今后有时机再详尽解读呢。

1)对象创造 

function Person() {}
Person.prototype.name = "Brittany";
Person.prototype.age = 23;
Person.prototype.job = "Web front-end engineer";
Person.prototype.getName = function() {
    console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();

console.log(p1.name);                    //Brittany
console.log(p2.name);                    //Brittany
console.log(p1.getName == p2.getName);   //true

实例中开创的属性会覆盖原型中的同名属性,无法改改原型中的属性。

p1.name = "Susan";
console.log(p1.name);                    //Susan

hasOwnProperty()检查测量试验壹脾性能是不是留存于实例中。

console.log(p2.hasOwnProperty("name"));  //false
p2.name = "koko";
console.log(p2.hasOwnProperty("name"));  //true
delete p2.name;
console.log(p2.hasOwnProperty("name"));  //false

isPropertyOf()

console.log(Person.prototype.isPrototypeOf(p1));   //true
console.log(Person.prototype.isPrototypeOf(p2));   //true

getPropertyOf()

console.log(Object.getPrototypeOf(p1) == Person.prototype);  //true
console.log(Object.getPrototypeOf(p1));                      //Person

六、总结

有关面向对象的基础知识大约正是这几个了。小编从最简便易行的创导三个指标初阶,解释了怎么我们必要构造函数与原型,通晓了这在那之中的细节,有利于我们在事实上支出中灵活的团组织谐和的对象。因为我们并非兼备的情景都会动用构造函数恐怕原型来成立对象,可能大家供给的靶子并不会注解多个实例,或然不用区分对象的品种,那么大家就能够选取更简明的措施。

大家还索要关切构造函数与原型的独家特色,有利于大家在创造对象时正确的推断大家的品质与格局到底是位于构造函数中依旧放在原型中。如果未有领悟通晓,那会给我们在实际上付出中变成非常大的干扰。

谈到底接下去的几篇小说,作者会挑多少个面向对象的例证,继续支持大家精通面向对象的实际上采取。

2 赞 4 收藏 1 评论

图片 5

2)原型与in操作符

in单独行使时,通过对象访问到一定属性时再次来到true,无论该属性存在于实例中照旧原型中。hasOwnProperty(),通过对象访谈到特定属性时重临true,且该属性存在于实例中。 

var p3 = new Person();
console.log("name" in p3);                //true
console.log(p3.hasOwnProperty("name"));   //false
p3.name = "insist";
console.log(p3.hasOwnProperty("name"));   //true

规定属性到底是存在于对象中,还是存在于原型中。如下函数hasPrototypePropery()再次回到true表示该属性存在于原型中,而不是存在于实例中。

function hasPrototypeProperty(object, name) {
    return !hasOwnProperty("name") && (name in object);
}

for..in循环,全部通过对象能够访问的,可枚举的(enumerated)属性,既富含存在于实例中的属性,也席卷存在于原型中的属性。

for(var prop in p1) {
    console.log(prop);                    //name age job sayName
}

Object.keys(),ECMAScript5的点子,取得对象上全体可枚举的属性,接收一个对象作为参数,重返值是一个分包全数可枚举属性的字符串数组。注意:Person.prototype也是指标。

var keys = Object.keys(Person.prototype);
console.log(keys);             //["name age job sayName"]
var p1 = new Person(); 
console.log(Object.keys(p1));  //[]
p1.name = "get";
console.log(Object.keys(p1));  //["name"]

Object.getOwnPropertyNames(),获得全体实例属性,无论它是或不是可枚举。

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys);     //["constructor", "name", "age", "job", "getName"] 
var keys_p1 = Object.getOwnPropertyNames(p1);
console.log(keys_p1);  //[]

3)更简洁的原型语法

function Person() {}
Person.prototype = {
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        return this.name;
    }
};
var friend = new Person();
console.log(friend instanceof Person);      //true
console.log(friend instanceof Object);      //true
console.log(friend.constructor == Person);  //false
console.log(friend.constructor == Object);  //false

在上边的代码中,将Person.prototype设置为等于一个目的字面量格局创立的新对象,最后结果一致。但有贰个差异:constructor属性不再指向Person了。每创制叁个函数,就能同不常间创造它的prototype对象,那些目的也会自行获取constructor属性。而笔者辈在此间运用的语法,本质上完全重写了暗中认可的prototype对象,由此constructor属性也就产生了新对象的constructor属性(指向Object构造函数),不再指向Person函数。此时固然instanceof操作符还是可以回到正确的结果,但通过constructor已经无法显著指标的品类了。

经过如下格局,将constructor手动设置为方便的值。

Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};

4)原型的动态性

在原型中查找值的长河是二次搜索,由此大家对原型对象所做的任何改造都能够即时从实例上呈现出来——纵然是先成立了实例后修改原型也如故如此。

var friend = new Person();
Person.prototype.sayHi = function() {
    console.log("hi");
};
friend.sayHi();

固然能够随时为原型加多属性和方法,而且修改能够立刻在富有目的实例中显示出来,但就算是重写整个原型对象,情状就不均等了。

function Person() {}
var friend = new Person();
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};
friend.getName();                  //error

假如创设实例放在重写原型对象之后,则不会报错。

5)原生对象的原型

负有原生引用类型(Object、Array、String)都在其构造函数的原型上定义了艺术,如:Array.prototype.sort()、String.prototype.subString(), 通过原生对象的原型能够博得富有暗中同意方法的援引,并能够定义新办法。

console.log(typeof Array.prototype.sort);        //function
console.log(typeof String.prototype.substring);  //function

String.prototype.startsWith = function(text) {
    return this.indexOf(text) == 0;
};
var msg = "Hello World";
console.log(msg.startsWith("Hello"));           //true

6)原型对象的标题

缺欠一:省略了为构造函数字传送递早先化参数这一环节,结果有所实例在暗中认可情形下将获得一致的属性值。

短处二:原型中持有属性被非常多实例分享,这种分享对于函数特别适合。对于包含基本值属性倒也说得过去,因为经过在实例上增多一个同名属性,可以掩饰原型中对应的性质。但对此满含引用类型值得属性来讲,难题相比较优异。

function Person() { }
Person.prototype = {
    constructor: Person,
    name: "Brittany",
    friends: ["pink", "judy", "sam"],
    age: 23,
    job: "Web front-end engineer",
    getName: function() {
        console.log(this.name);
    }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("leo");
console.log(person1.friends);           //["pink", "judy", "sam", "leo"]
console.log(person2.friends);           //["pink", "judy", "sam", "leo"]
console.log(Person.prototype.friends);  //["pink", "judy", "sam", "leo"]
person1.age = 35;
console.log(person1.age);           //35
console.log(person2.age);           //23
console.log(Person.prototype.age);  //23

person1的friends属性修改影响了person2的friends,但是person1的age属性修改并未有影响person2的age属性。

案由在于:friends数组存在于Person.prototype中而非person1中,因而修改也会通过person2.friends(与person1.friends指向同一个数组)反映出去。而age属性在person第11中学也存在一份,修改的age属性只是修改person第11中学的,并不能够改改Person.prototype中的age属性。

6. 结合使用构造函数形式和原型方式

构造函数方式用于定义实例属性,而原型情势用于定义方法和分享的属性。那样,每一种实例都会有和谐的一份实例属性的别本,但又同不时间分享着对章程的引用。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["aa", "bb", "cc"];
}
Person.prototype = {
    constructor: Person,
    sayName: function() {
        console.log(this.name);
    }
};
var person1 = new Person("Brittany", 23, "Web front-end Engineer");
person1.friends.push("dd");     //["aa", "bb", "cc", "dd"]
console.log(person1.friends);
var person2 = new Person("Sam", 26, "Web front-end Engineer");
console.log(person2.friends);   //["aa", "bb", "cc"]
console.log(person1.friends == person2.friends);  //false
console.log(person1.sayName == person2.sayName);  //true

 

 

时间:2014-10-21

地点:合肥

援引:《JavaScript高等程序设计》 

编辑:关于计算机 本文来源:原型与原型链,对象的创建

关键词: