实例与原型
一、当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
| 【示例】通过new操作符来创建基于原型对象的doSomething实例。这样可以获得doSomething这个函数的一个实例对象。一些属性就可以添加到该原型对象中。```javascript function doSomething () {} doSomething.prototype.foo = ‘bar’ // add a property onto the prototype
var doSomeInstancing = new doSomething() doSomeInstancing.prop = ‘some value’ // add a property onto the object
console.log(doSomeInstancing)
1、运行结果如下:```javascript
{
prop: "some value",
__proto__: {
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}
|
| —- |
| 【示例】```javascript function Person() {
}
Person.prototype.name = ‘Kevin’;
var person = new Person();
person.name = ‘Daisy’; console.log(person.name) // Daisy
delete person.name; console.log(person.name) // Kevin
1、我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。<br />2、但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。<br />3、但是万一还没有找到呢?原型的原型又是什么呢? |
| --- |
<a name="qo4eV"></a>
# 原型的原型
一、原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是:
```javascript
var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin
二、其实原型对象就是通过 Object 构造函数生成的,实例的 proto 指向构造函数的 prototype ,所以我们再更新下关系图:
原型链
一、Object.prototype 的原型是null
console.log(Object.prototype.__proto__ === null) // true
1、 null 究竟代表了什么呢?
(1)引用阮一峰老师的《undefined与null的区别》就是:null 表示“没有对象”,即该处不应该有值。
2、所以 Object.prototype.proto 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。
3、所以查找属性的时候查到 Object.prototype 就可以停止查找了。
二、最后一张关系图也可以更新为:
1、图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
| 【示例】```javascript function doSomething () {} doSomething.prototype.foo = ‘bar’
var doSomeInstancng = new doSomething() doSomeInstancing.prop = ‘some value’
console.log(‘doSomeInstancing.prop ‘ + doSomeInstancing.prop) // some value console.log(‘doSomeInstancing.foo: ‘ + doSomeInstancing.foo) // bar console.log(‘doSomething.prop: ‘ + doSomething.prop) // undefined console.log(‘doSomething.foo: ‘ + doSomething.foo) // undefined console.log(‘doSomething.prototype.prop: ‘ + doSomething.prototype.prop) // undefined console.log(‘doSomething.prototype.foo: ‘ + doSomething.prototype.foo) // bar
|
| --- |
| 【示例】下列说法正确的是()<br />A. 每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法<br />B. 对象的__proto__指向自己构造函数的prototype<br />C. Object.prototype. __proto__=== null,说明原型链到Object.prototype终止<br />D. 表达式 Function.prototype.__proto__.__proto__ === null 的运行结果为true<br />**答案**:BCD<br />**解析**:如果是使用Object.create(null)创建的空对象,是没有对应原型对象的。所以选项A错误 |
| --- |
| 【示例】以下代码,在浏览器中执行的结果是```javascript
var A = {n:4399};
var B = function(){this.n = 9999};
var C = function(){var n = 8888};
B.prototype = A;
C.prototype = A;
var b = new B();
var c = new C();
A.n++;
console.log(b.n);
console.log(c.n);
A. 9999 8889
B. 10000 8888
C. 4400 8889
D. 9999 4400
E. 4400 4400
答案:D
解析:考察点:原型链的继承
new运算的具体执行过程:
1)创建一个空对象
2)把这个空对象的proto指向构造函数的prototype
3)把这个空对象赋值给this
4)执行构造函数内的代码,注意此时的this指向新对象,this.n=9999 等价于b.n=9999;
然后访问b.n,存在,直接输出b.n。
再去访问c.n,不存在,通过原型链proto向上寻找,c.proto指向C.prototype也就是A,所以就是输出A.n |
| —- |
| 【示例】```javascript Function.prototype.a = ‘a’; Object.prototype.b = ‘b’; function Person(){}; var p = new Person(); console.log(‘p.a: ‘+ p.a); // p.a: undefined console.log(‘p.b: ‘+ p.b); // p.b: b 问为什么?
解析:有不少同学第一眼看上去就觉得很疑惑,p不是应该继承了Function原型里面的属性吗,为什么p.a返回值是undefined呢? 其实,只要仔细想一想就很容易明白了,Person函数才是Function对象的一个实例,所以通过Person.a可以访问到Function 原型里面的属性,但是new Person()返回来的是一个对象,它是Object的一个实例,是没有继承Function的,所以无法访问 Function原型里面的属性。但是,由于在js里面所有对象都是Object的实例,所以,Person函数可以访问到Object原型里面的 属性,Person.b => 'b' |
| --- |
| 【示例】关于这段代码的描述,正确的是: ```javascript
var F=function(){};
Object.prototype.a=function(){};
Function.prototype .b=function(){};
var f=new F();
A. f能取到a,但取不到b
B. f能取到a,b
C. F能取到b,不能取到a
D. F能取到a,不能取到b
答案:A
解析:这个问题涉及到js的原型继承
1. f.proto === f[的构造函数].prototype === F.prototype
2. F.prototype.proto === (F.prototype)[的构造函数].prototype === Object.prototype (所以a能够 通过f.a访问)
3. f.constructor === F (f没有constructor,所以f顺着proto去找这个方法,在F.prototype中找到constructor这个方法,于是f.constructor有效)(f.constructor === F.prototype.constructor === F, f和F.prototype 都是F的实例)
4. F.proto === F[的构造函数].prototype === Function.prototype (所以b可以通过, f.constructor.b访问到)
注意:
(F.prototype)[的构造函数] === Object
F[的构造函数] === Function
多啰嗦一句( js 的继承靠的是proto ,并不是prototype) |
| —- |
JavaScript对象模型
一、红色虚线表示隐式Prototype链。
(图片来源于博客园RicCC)
20220624-aSuncat:自己整理后如下
- 图中有好几个地方提到build-in Function constructor,这是同一个对象,可以测试验证:
这说明了几个问题: Function指向系统内置的函数构造器(build-in Function constructor);Function具有自举性;系统中所有函数都是由Function构造。//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 Function==Function.constructor //result: true Function==Function.prototype.constructor //result: true Function==Object.constructor //result: true //Function also equals to Number.constructor, String.constructor, Array.constructor, RegExp.constructor, etc. function fn(){} Function==fn.constructor //result: true
2. 左下角的obj1, obj2…objn范指用类似这样的代码创建的对象:function fn1(){};`` var obj1=new fn1();
这些对象没有本地constructor方法,但它们将从Prototype链上得到一个继承的constructor方法,即fn.prototype.constructor
,从函数对象的构造过程可以知道,它就是fn本身了。
右下角的obj1, obj2…objn范指用类似这样的代码创建的对象:var obj1=new Object();
或var obj1={};
或var obj1=new Number(123);
或obj1=/\w+/;
等等。所以这些对象Prototype链的指向、从Prototype链继承而来的constructor的值(指它们的constructor是build-in Number constructor还是build-in Object constructor等)等依赖于具体的对象类型。另外注意的是,var obj=new Object(123);
这样创建的对象,它的类型仍然是Number,即同样需要根据参数值的类型来确定。
同样它们也没有本地constructor,而是从Prototype链上获得继承的constructor方法,即build-in * constructor,具体是哪一个由数据类型确定。
3. 关于图中Prototype链的补充说明:
(1)Object.prototype是整个链的终结点,它的内部[[Prototype]]为null。
(2)所有函数的Prototype链都指向Function.prototype。
① Function的Prototype链指向Function.prototype,这是规范要求的,因为设计者将Function设计为具有自举性。Function的Prototype链这样设计之后,Function.constructor==Function, Function instanceOf Function都为true。另外Function已经是最顶层的构造器,但Function本身也是一个函数对象,它必然是由某个东西创建出来的,这样自举在语义上合情合理。
② Function.prototype的Prototype链指向Object.prototype,这也是规范强制要求的。
首先Function.prototype是Function的一个实例对象(typeof Function.prototype可以知道它是一个Function,instanceOf无法通过测试,因为Prototype链在内部被额外设置了),所以按照Prototype的规则,Function.prototype的内部[[Prototype]]值应当为Function.prototype这个对象,即它的Prototype链指向自己本身。这样一方面在Prototype链上造成一个死循环,另一方面它本身成为了一个终结点,结果就是所有函数对象将不是派生自Object了。加上这个强制要求之后,Prototype链只有唯一的一个终结点。 ```javascript // 原型链
e -> E.prototype -> Object.prototype -> Null E -> Function.prototype -> Object.prototype -> Null Function -> Function.prototype -> Object.prototype -> Null
```javascript
Function.__proto__ === Function.prototype; // true
Object.getPrototypeOf(Function) === Function.prototype; // true
Function.__proto__ === Object.prototype; // false
- 因为Function.prototype是一个函数对象,所以它应当具有显示的prototype属性,即Function.prototype.prototype,但只有FireFox中可以访问到,IE、Opera、Safari都无法访问。所以图中用了个表示不存在的符号。
5. 用户自定义函数(user defined functions)默认情况下[[Prototype]]值是Object.prototype,即它的隐式Prototype链指向Object.prototype,所以图中就这样表示了,但并不代表总是这样,当用户设置了自定义函数的prototype属性之后,情况就不同了。