一、创建实例

  1. function Employee () {
  2. this.name = "";
  3. this.dept = "general";
  4. }
  5. function WorkerBee() {
  6. Employee.call(this);
  7. this.projects = [];
  8. }
  9. 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 />原型继承的特点:它是把父类中私有+公有的都继承到了子类原型上(子类公有的),具体见下图![](https://cdn.nlark.com/yuque/0/2021/jpeg/355497/1637545481338-b4e2049d-f7fd-4792-8343-34721b726f4a.jpeg#clientId=u9dd4d320-7ffe-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc5d52c02&margin=%5Bobject%20Object%5D&originHeight=475&originWidth=885&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u5b0a91c4-0f15-4fa8-bec7-80ed99c4ebe&title=)<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对象不会继承这个新属性 ```