创建对象的几种方式:
// 第一种方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
// 第二种方式:构造函数
var M = function (name) { this.name = name; };
var o3 = new M('o3');
// 第三种方式:Object.create
var p = {name: 'p'};
var o4 = Object.create(p);
上面几种创建对象的方式,打印出如下结果:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>原型链</title>
</head>
<body>
<script type="text/javascript">
// 第一种方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});
// 第二种方式:构造函数
var M = function (name) { this.name = name; };
var o3 = new M('o3');
// 第三种方式:Object.create
var p = {name: 'p'};
var o4 = Object.create(p);
M.prototype.say = function () {
console.log('say hi');
};
var o5 = new M('o5');
var new2 = function (func) {
var o = Object.create(func.prototype);
var k = func.call(o);
if (typeof k === 'object') {
return k;
} else {
return o;
}
};
</script>
</body>
</html>
构造函数、原型、实例、原型链
- 构造函数是通过 new 运算符来生成一个实例对象;
- 构造函数也是函数,任何函数都可以作为构造函数;
- 函数都有一个
prototype
属性。JS 引擎会在你声明一个函数时自动加上这一个属性( prototype );
原型链:每一个实例对象都有一个 __proto__
属性,它指向的是这个对象的原型对象,原型对象也是一个对象,它也会有 __proto__
属性,这样就形成了一个链条,称之为 原型链。
M.prototype.constructor === M // true
o3.__proto__ === M.prototype // true
总结
- 函数才会有
prototype
属性,对象是没有的; 只有实例对象才会有
__proto__
属性。但是函数也有__proto__
属性,是为什么呢???因为函数也是一个对象,所以它也会有__proto__
属性。M函数也是 Function 构造函数的一个实例对象。M.__proto__ === Function.prototype // true
实例是通过构造函数来生成的,它是怎么和原型对象产生联系的呢???其实就是通过构造函数上的
prototype
属性。instanceof 的原理
instanceof
运算符:左操作数是一个对象,右操作数是标识对象的类。若左对象是右对象的实例,则表达式返回true
,否则返回false
。
实例对象是通过构造函数来生成。
实例对象上的__proto__
属性,是通过构造函数上的prototype
属性来和原型进行关联的。
instanceof 原理:实例对象的__proto__
属性和构造函数的prototype
属性是否是同一个引用。
注意:只要是原型链上的都会看成是实例对象的构造函数。 ```javascript // 代码延用上侧代码举例
o3 instanceof M // true
o3 instanceof Object // true
o3.proto === M.prototype // true M.prototype.proto === Object.prototype // true
虽然`Object `不是` o3`的构造函数,但是它存在于原型链上,所以说用 `instanceof `来判断 `o3`的构造函数是不是 `Object `是不准确的。怎么准确判断,方法如下:
```javascript
o3.__proto__.constructor === Object // false
再次举例说明:当通过 instanceof
来判断一个对象是否是一个类的实例时,这个判断也会包含对父类的检测。虽然 instanceof
的右操作数是构造函数,但计算过程实际是检测了对象的继承关系。
代码举例如下:
var d = new Date();
d instanceof Date; // true d是Date的实例
d instanceof Object; // true 所有对象都是Object的实例
追加知识
L instanceof R
// instanceof运算时,通过判断L的原型链上是否存在R.prototype,若存在返回 true ,否则返回 false
// L.__proto__.__proto__.__proto__.__proto__... === R.prototype
具体可查看 手写 instanceof instanceof
运算规则为:instanceof 检测左侧的 proto 原型链上,是否存在右侧的 prototype 原型。
图解构造器 Function
和 Object
关系:
TODO:图
// 1:构造器 Function 的构造函数是自身
// Function.constructor ===> ƒ Function() { [native code] }
Function.constructor === Function // true
// 2:构造器 Object 的构造函数是 Function(由此可知:所有构造器的构造函数都指向 Function)
// Object.constructor ===> ƒ Function() { [native code] }
// Array.constructor ===> ƒ Function() { [native code] }
Object.constructor === Function // true
// 3:构造器 Function 的 __proto__ 是一个特殊的匿名函数
Function.__proto__ // ƒ () { [native code] }
// 4:这个特殊匿名函数的 __proto__ 指向 Object 的 prototype 原型
Function.__proto__.__proto__ === Object.prototype // true
// 5:Object 的 __proto__ 指向 Function 的 prototype,也就是 3 中说的特殊匿名函数
Object.__proto__ === Function.prototype // true
Function.prototype === Function.__proto__ // true
Object.__proto__ === Function.__proto__ // true
当构造器 Object、Function 用 instanceof
来比较
// why??????
Object instanceof Function // true
Function instanceof Object // true
// 答案:依据 instanceof 的运算规则就可明白
Object.__proto__ === Function.prototype
Function.__proto__.__proto__ === Object.prototype // true
小结:
- 所有构造器的构造函数(
constructor
)都指向Function
; Function
的prototype
指向一个特殊的匿名函数,此匿名函数的__proto__
指向Object.prototype
。即:Function.prototype.__proto__ === Object.prototype
的结果为true
。Object
是new
出来实例对象的基类,function Object() { [native code] }
;Function
是作为众多function
出来的函数的基类,function Function() { [native code] }
;- 构造函数的
__proto__
(包括Function
和Object
)都指向Function.prototype
; - 原型对象的
__proto__
都指向Object.prototype
; Object.prototype.__proto__ === null
;- 所有对象(包括:对象、函数)都有
__proto__
属性,指向构造该对象的构造函数的原型; - 只有函数
function
才具有prototype
属性。此属性是一个指针,指向一个对象,此对象的用途就是包含所有实例共享的属性和方法(此对象称为:原型对象)。原型对象也有一个属性,叫做constructor
,这个属性包含一个指针,指回原构造函数。new 运算符
模拟 new 运算符:var new2 = function (func) {
// func 是一个构造函数
var o = Object.create(func.prototype);
var k = func.call(o);
if (typeof k === 'object') {
return k;
} else {
return o;
}
};
注意:o6 = new2(M) // M
o6 instanceof M // true
o6 instanceof Object // true
o6.__proto__.constructor === M // true
Object.create()
创建对象的对象是用原型链来连接的,o4 的__proto__
指向这个p对象 ,也就是指向构造函数的prototype
属性,o4.__proto__ === p // true
怎么手写一个 new 函数???
isPrototypeOf 、instanceof、hasOwnProperty 三者区别
isPrototypeOf
作用:检测一个对象是否是另一个对象的原型。或者说 一个对象是否被包含在另一个对象的原型链中。 ```javascript var p = {x:1};//定义一个原型对象
var o = Object.create(p);//使用这个原型创建一个对象
p.isPrototypeOf(o);//=>true:o继承p
Object.prototype.isPrototypeOf(p);//=> true p继承自Object.prototype
上例摘自《Javascript 权威指南》,简单解释以下就是:每一个 JS 对象都和原型关联,每一个对象都从原型继承属性。所有通过对象 **直接量** 创建的对象都使用 `Object.prototype` 为他们的原型,因此`p`是继承自 `Object.prototype` ,因此在`p`的原型链中一定存在 `Object.prototype`。<br />`Object.create()` 方法是创建一个新对象,第一个参数是这个新对象的原型。所以上例 创建的 `o`对象,它的原型是 `p`。
```javascript
function Animal(){
this.species = "动物";
};
var eh = new Animal();
Animal.prototype.isPrototypeOf(eh) //=>true
上例是通过 new
创建了实例对象 eh
,使用构造函数 Animal
的 prototype
作为它(eh)的原型。
综上两例,发现调用 isPrototypeOf
的三种方式如下:
p.isPrototypeOf(o); // true
Object.prototype.isPrototypeOf(p); // true
Animal.prototype.isPrototypeOf(eh); // true
**isPrototypeOf**
总结如下:
- 通过
Object.create()
方法创建的对象,使用第一个参数作为其原型; - 通过对象 直接量 的对象,使用
Object.prototype
作为其原型; - 通过
new
创建的对象,使用构造函数的prototype
属性作为其原型。isPrototypeOf 和 instanceOf 比较
```javascript // A构造函数的prototype对象是否在B的原型链上 B instanceof A
// A对象是否在B的原型链上 A.isPrototypeOf(B)
举例:
```javascript
class A{}
class B extends A{}
let b = new B();
// 这两个类之间的关系
B.__proto__ === A //true
B.prototype.__proto__=== A.prototype //true
b.__proto__ === B.prototype //true
为什么会出现如下代码结果。
首先类的继承是按如下方式实现的:
class A {
}
class B {
}
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
const b = new B();
// B的实例继承A的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
setPrototypeOf()
实现如下:
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
所以得出前两个结果,也就是如下代码结果:
// 这两个类之间的关系
B.__proto__ === A //true
B.prototype.__proto__ === A.prototype //true
而new
操作是按如下方式实现的:
var b = new Object();
b.__proto__ = B.prototype;
B.call(b);
所有得到第三个表达式: b.__proto__ === B.prototype //true
。
接下来看看 instanceof
和 isPrototypeOf
的表现:
A.isPrototypeOf(B);//true
A.isPrototypeOf(b);//false
b instanceof B;//true
b instanceof A;//true
B instanceof A;//false
再次回头看看三个判断表达式给出的结果就可知:
A对象在B的原型链上,不在b的原型链上,所以:
A.isPrototypeOf(B);//true
A.isPrototypeOf(b);//false
A,B的prototype在b的原型链上而不在B的原型链上,所以:
b instanceof B;//true
b instanceof A;//true
B instanceof A;//false
小结:Y instanceof X
判断是的是X
的prototype
是否在Y
的原型链上,而实例的原型链(__proto__
)指向的就是其构造函数的prototype
,即Y instanceof X
判断Y
是否是X
的一个实例(若Y
是X
的实例,那他也是X
父类的实例)。
而X.isPrototypeOf(Y)
判断的是X
对象是否在Y
的原型链上,同样Y
继承X
的关系是X
对象在Y
对象的原型链上,即X.isPrototypeOf(Y)
判断X是否继承至Y
hasOwnProperty
对象的 hasOwnProperty()
方法用来检测该属性是否是对象的自有属性。若是对象的自有属性,则返回 true
,否则返回 false
,false
说明该属性是继承属性。
function Animal(){}//定义Animal构造函数
Animal.prototype = {//定义Animal原型
species:"动物",
say:function(){
console.log('i can say word');
}
}
function Cat(name,color){//定义构造函数Cat
this.name = name;
this.color = color;
}
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;// Cat继承Animal 用F空对象作为媒介
var eh = new Cat('lili','white');//实例化对象
console.log('say' in eh)//=>true
console.log('name' in eh)//=>true
console.log('color' in eh)//=>true
console.log('species' in eh)=>true
console.log(eh.hasOwnProperty('say'))=>false 由于say为继承属性 非自有属性
console.log(eh.hasOwnProperty('species'))=>false 由于species为继承属性 非自有属性
console.log(eh.hasOwnProperty('name'))=>true
console.log(eh.hasOwnProperty('color'))=>true
for(var key in eh){
console.log(key);
if(eh.hasOwnProperty(key)){
console.log(key) //=>species say name color
}
}