一、创建实例
function Employee () {
this.name = "";
this.dept = "general";
}
function WorkerBee() {
Employee.call(this);
this.projects = [];
}
WorkerBee.prototype = Object.create(Employee.prototype);
// 创建一个mark对象作为WorkerBee的实例
var mark = new WorkerBee
1、当JavaScript执行new操作符时
(1)会先创建一个普通对象,
(2)并将这个普通对象中的[[prototype]]指向WorkerBee.prototype,
(3)然后再把这个普通对象设置为执行WorkerBee构造函数时this的值。
该普通对象的[[prototype]]决定其用于检索属性的原型链。
2、当构造函数执行完成后,所有的属性都被设置完毕,JavaScript返回之前创建的对象,通过赋值语句将它的引用赋值给变量mark
3、这个过程不会显式地将mark所继承的原型链中的属性作为本地属性放在mark对象中。然后参考【对象属性查找机制】。
(1)这样,mark对象中将具有如下的属性和对应的值
mark.name = ''
mark.dept = 'general'
mark.projects = []
(2)mark对象从mark.proto中保存的原型对象里继承了name和dept属性。并由WorkerBee构造函数为projects属性设置了本地值。
4、这就是JavaScript中的属性和属性值的继承。
5、由于这些构造器不支持为实例设置特定的值,所以这些属性值仅仅是创建自WorkerBee的所有对象所共享的默认值。当然这些属性的值是可以修改的,所以您可以为mark指定特定的信息,如下:
mark.name = 'Doe, Mark'
mark.dept = 'admin'
mark.projects = ['navigator']
继承属性的方式
原型继承
见原型继承#原型继承:https://www.yuque.com/tqpuuk/yrrefz/fh9b05
call() /apply()
一、继承的另一种途径是使用call() / apply()方法。
二、下面的方式是等价的
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
// 使用javascript的call()方法相对明了一些,因为无需base方法了
function Engineer (name, projs, mach) {
WorkerBee.call(this, name, "engineering", projs);
this.machine = mach || "";
}
| 【示例】
1、原型继承```javascript
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
this.y = 200
}
B.prototype = new A;
B.prototype.constructor = B
原型继承是我们JS中最常用的一种继承方式<br />子类B想要继承父类A中的所有属性和方法(公有+私有),只需要让B.prototype = new A;即可<br />原型继承的特点:它是把父类中私有+公有的都继承到了子类原型上(子类公有的),具体见下图<br />核心:原型继承并不是把父类中的属性和方法克隆一份一模一样的给B而是让B和A之间增加了原型链的链接,以后B的实例n想要用A中的getX方法,需要一级一级的向上查找来使用。<br />2、call继承<br />把父类私有的属性和方法克隆一份一模一样的 作为子类私有的属性```javascript
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this)//A.call(n) 把A执行让A中的this变成n
}
3、冒充对象继承```javascript function A(){ this.x = 100; } A.prototype.getX = function(){ console.log(this.x); } function B(){ var temp = new A; for(var key in temp){ this[key] = temp[key] } temp = null } var n = new B;
冒充对象继承:把父类公有的+私有的克隆一份一模一样的给子类私有的<br />4、混合模式继承:原型继承+call继承(第二常用)```javascript
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this);
}
B.prototype = new A;
B.prototype.constructor = B;
缺点:A执行了两遍,且A中x既是私有属性,又是公有属性
5、寄生组合式继承:解决上面私有的重复问题```javascript
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
A.call(this);
}
B.prototype = Object.create(A.prototype);//IE6-8不兼容 ,可以自己写一个Object.create方法
B.prototype.constructor = B;
6、中间类继承法->不兼容(移动端开发考虑)<br />A.__proto__ = B.prototype |
| --- |
<a name="DwGWC"></a>
# 本地值、继承值
一、先参考(【见】对象的属性#访问属性-对象属性查找机制:[https://www.yuque.com/tqpuuk/yrrefz/kh1pz7](https://www.yuque.com/tqpuuk/yrrefz/kh1pz7))。这些步骤的结果依赖于你是如何定义的。<br />二、定义
```jsx
function Employee () {
this.name = ''
this.dept = 'general'
}
function WorkerBee () {
this.projects = []
}
Worker.prototype = new Employee
1、基于这些定义,假定通过如下的语句创建WorkerBee的实例amy
var amy = new WorkerBee
(1)amy对象将具有一个本地属性projects。name和dept则不是amy对象的本地属性,而是从amy对象的proto属性获得的。因此,amy将具有如下的属性
amy.name == ''
amy.dept == 'general'
amy.projects == []
2、假设修改了与Employee的相关联原型中的name属性的值
Employee.prototype.name = 'Unknown'
(1)乍一看,可能觉得新的值会传播给所有Employee的实例。
(2)然而,并非如此。
(3)在创建Employee对象的任意实例时,该实例的name属性将获得一个本地值(空的字符串)。
(4)这就意味着在创建一个新的Employe对象作为WorkerBee的原型时,WorkerBee.prototype的name属性将具有一个本地值。
(5)因此,当JavaScript查找amy对象(WorkerBee的实例)的name属性时,JavaScript将找到WorkerBee.prototype中的本地值。
(6)因此,也就不会继续在原型链中向上找到Employee.prototype了。
3、如果希望对象的属性具有默认值,并且希望在运行时修改这些默认值,且希望该值被所有该对象的后代所继承,应该在对象的原型中设置这些属性,而不是在构造函数中。
【示例1】以下情况,amy的name属性将为“Unknown”
function Employee () {
// this.name = '' , 不能在构造函数中定义name属性,这样不会被Employee对象的后代所继承
this.dept = 'general'
}
Employee.prototype.name = '' // 将属性添加到原型中
function WorkerBee () {
this.projects = []
}
判断实例的关系
一、特殊的proto属性是在构建对象时设置的;设置为构造器的prototype属性的值。
【示例1】表达式new Foo()将创建一个对象,其 proto == Foo.prototype。
1、修改Foo.prototype的属性,将改变所有通过new Foo()创建的对象的属性的查找。
二、每个对象都有一个proto对象属性(除了Object);每个函数都有一个prototype对象属性。因此,通过“原型继承”,对象与其它对象之间形成关系。
1、通过比较对象的proto属性和函数的prototype属性可以检测对象的继承关系。
三、JavaScript提供了便捷方法:instanceof (of的o是小写)操作符可以用来将一个对象和一个函数做检测,如果对象继承自函数的原型,则该操作符返回真
【示例1】
var f = new Foo()
var isTrue = (f instanceof Foo)
【示例2】创建Engineer对象如下
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
}
function WorkerBee (name, dept, projs) {
this.base = Employee
this.base(name, dept)
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;
function Engineer (name, projs, mach) {
this.base = WorkerBee
this.base(name, 'engineering', projs)
this.machine = mach || ''
}
Engineer.prototype = new WorkerBee;
var chris = new Engineer('Pigman, Chris', ['jsd'], 'fiji')
// 以下语句均为真 true
chris instanceof Engineer;
chris instanceof WorkerBee;
chris instanceof Employee;
chris instanceof Object;
// 以下语句均为真 true
chris.__proto__ == Engineer.prototype;
chris.__proto__.__proto__ == WorkerBee.prototype;
chris.__proto__.__proto__.__proto__ == Employee.prototype;
chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
1、基于上述例子,可以写出如下所示的instanceOf函数
function instanceOf (object, constructor) {
while (object != null) {
if (object == constructor.prototype) {
return true
}
if (typeof object == 'xml') {
return constructor.prototype == XML.prototype // 检查对象的类型是否为'xml'的目的自安于解决新近版本的JavaScript中表达XML对象的特异之处
}
object = object.__proto__
}
return false
}
// 以下表达式为真 true
instanceOf (chris, Engineer)
instanceOf (chris, WorkerBee)
instanceOf (chris, Employee)
instanceOf (chris, Object)
// 以下表达式为假 false
instanceOf (chris, SalesPerson)
构造器中的全局信息
一、在创建构造器时,在构造器中设置全局信息要小心。
二、在构造器中设置全局信息,以下是错误示例
【示例1】假如希望为每一个雇员分配一个唯一标识。
1、可能会为Employee使用如下定义
var idCounter = 1
function Employee (name, dept) {
this.name = name || ''
this.dept = dept || 'general'
this.id = idCounter++
}
2、基于该定义,在创建新的Employee时,构造器为其分配了序列中的下一个标识符。然后递增全局的标识符计算器。
var victoria = new Employee('Pigbert, Victoria', 'pubs')
console.log(victoria.id) // 1
var harry = new Employee('Tschopik, Harry', 'sales')
console.log(harry.id) // 2
(1)乍一看似乎没问题。但是,无论什么目的,在每一次创建Employee对象时,idCounter都将被递增一次。如果创建整个Employee层级结构,每次设置原型的时候,Employee构造将都将被调用一次
var idCounter = 1;
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
this.id = idCounter++;
}
function Manager (name, dept, reports) {...}
Manager.prototype = new Employee;
function WorkerBee (name, dept, projs) {...}
WorkerBee.prototype = new Employee;
function Engineer (name, projs, mach) {...}
Engineer.prototype = new WorkerBee;
function SalesPerson (name, projs, quota) {...}
SalesPerson.prototype = new WorkerBee;
var mac = new Engineer("Wood, Mac");
console.log(mac.id) // 5
3、依赖于应用程序,计数器额外的递增可能有问题,也可能没问题。
三、在构造器中设置全局信息,以下是正确的方法
- 方法1:构造器中增加判断条件
【示例1】如果要准确的计数器,以下构造器可以作为一个可行的方案
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
if (name)
this.id = idCounter++;
}
1、在用作原型而创建新的Employee实例时,不会指定参数。
2、使用构造器定义,如果不指定参数,构造器不会指定标识符,也不会递增计数器。
3、如果想让Employee分配到标识符,则必须为雇员指定姓名。
var idCounter = 1;
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
if (name)
this.id = idCounter++;
}
function WorkerBee (name, dept, projs) {...}
WorkerBee.prototype = new Employee;
function Engineer (name, projs, mach) {...}
Engineer.prototype = new WorkerBee;
var mac = new Engineer("Wood, Mac");
console.log(mac.id) // 1
- 方法2:创建一个Employee的原型对象的副本以分配给WorkerBee
WorkerBee.prototype = Object.create(Employee.prototype) // 而不是 WorkerBee.prototype = new Employee
没有多重继承
一、多重继承:对象可以从无关的多个父对象中继承属性和属性值。
二、JavaScript不支持多重继承。
三、JavaScript属性值的继承是在运行时通过检索对象的原型链来实现的。因为对象只有一个原型与之相关,所以JavaScript无法动态地从多个原型链中继承。
四、在JavaScript中,可以在构造器函数中调用多个其它的构造器函数。这一点造成了多重继承的假象
【实例1】 ```jsx function Hobbyist (hobby) { this.hobby = hobby || ‘scuba’ }
function Engineer (name, projs, mach, hobby) { this.base1 = WorkerBee this.base1(name, ‘engineering’, projs) this.base2 = Hobbyist this.base2(hobby) this.machine = mach || ‘’ }
Engineer.prototype = new WorkerBee
var dennis = new Engineer(‘Doe, Dennis’, [‘collabra’], ‘hugo’)
// dennis对象有如下属性 dennis.name == “Doe, Dennis” dennis.dept == “engineering” dennis.projects == [“collabra”] dennis.machine == “hugo” dennis.hobby == “scuba” // dennis从Hobbyist构造器中获得了hobby属性
// 添加了一个属性到Hobbyist构造器的原型 Hobbyist.prototype.equipment = [‘mask’, ‘fins’, ‘regulator’, ‘bcd’] console.log(dennis.equipment) // undefined ,dennis对象不会继承这个新属性 ```