[重点] 原型链详解
回顾js 内存分配以及垃圾回收
- 垃圾回收查看笔记: js 内存篇
js 变量内存存放位置
- 基本类型的变量存放在栈中
- 引用对象(包括new 出来的对象)的值放在堆中, 而在栈中存放一个指针指向堆中的值(所以表面上变量都是存放在栈)
![](https://images2015.cnblogs.com/blog/699296/201603/699296-20160319221028303-1822175444.png)
js 几个重要的概念
Function: 函数对象
Object: 对象
prototype 属性 :
- 指向一个对象(原型对象), 原型对象是js为了实现继承和代码复用, 而暴露给程序员的类似原型链的一个开放接口
- 原型对象包括程序员定义的复用的方法和属性等, 包括constructor属性
- 每一个函数对象在创建时都会创建一个prototype属性(只有函数对象有), 指向改函数的原型对象
__proto__ 属性 :
- 指向一个对象, 是内部属性(chrome firefox 能用 __proto__的形式访问), 可以理解为js 内部真正的原型链
- 每个对象都会在其内部初始化__proto__,当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性, 直到为空
- Object.prototype.__proto__ 指向 null, 原型链从这里结束
Function 和 Object 的关系
[注意]
- 图中代码执行可得
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Object.O1 = "" ; // Object对象添加属性O1 Object.prototype.Op1 = "" ; // Object原型对象添加属性Op1 Function.F1 = "" ; Function.prototype.Fp1 = "" ; function Cat() {} Cat.C1 = "" ; Cat.prototype.Cp1 = "" ; var cat = new Cat(); // cat.__proto__ = Cat.prototype var o = {}; console.log(cat); // Cat {Cp1: "", Op1: ""} console.log(o); // Object {Op1: ""} |
- Function.__proto__ 和 Object.__proto__和 普通函数对象Cat.__proto__ 都指向Function.prototype对象, 也就是我们说的 函数字面量创建的对象都连接到 Function.prototype
- Function.prototype.__proto__ 和 普通函数对象(继承会影响)Cat. prototype.__proto__ 都指向Object.prototype对象(父对象的.prototype), 也就是我们说的 对象字面量创建的对象都连接到 Object.prototype
- 普通对象的__proto__指向 Object.prototype对象
new 的过程: p.__proto__ = Person.prototype;
- 先看以下代码
1 2 | var Person = function () {}; var p = new Person(); |
- new 的过程分为3步
1 2 3 | var p = {}; // 初始化一个空对象p。 p.__proto__ = Person.prototype; // 关键!!!!!!!! Person.call(p); // 也就是说构造p,也可以称之为初始化p。 |
- 更复杂的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Person 类 var Person = function () {}; Person.prototype.Say = function () { alert( "Person say" ); } Person.prototype.Salary = 50000; // Programmer 类 var Programmer = function () { }; Programmer.prototype = new Person(); // Programmer.prototype.__proto__ == Person.prototype Programmer.prototype.WriteCode = function () { alert( "programmer writes code" ); }; Programmer.prototype.Salary = 500; //测试 var p = new Programmer(); // p.__proto__ = Programmer.prototype,p.__proto__.__proto__ = Person.prototype p.Say(); p.WriteCode(); alert(p.Salary); |
- 在p.say() 时, p中木有say(), 所以去p.__proto__找, 发现也没有就去p.__proto__.__proto__找
构造函数返回值
概念
在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。而在js中构造函数可以有返回值也可以没有。
返回类型:
没有返回值: 返回实例化的new对象
返回值是非引用类型(如基本类型): 返回其实例化的new对象
返回值是引用类型: 返回这个引用类型。
1 2 3 4
6 7 8 9 10 11 12 13 14 15 16 17 | // 1 无返回值 function F() {} // undefined new F(); // F{}, 无影响 // 2 返回返回值是基本类型 function F() { // undefined return true ; } new F(); // F{}, 无影响 // 3 返回返回值是引用类型 function F() { // undefined return { a: 1 }; } new F(); // Object {a: 1}, new 返回的是 该引用对象 |
instanceof 运算符
- 以下代码, 会不会觉得很晕
1 2 3 4 5 6 7 8 9 | console.log(Object instanceof Object); //true console.log(Function instanceof Function); //true console.log(Number instanceof Number); //false console.log(String instanceof String); //false console.log(Function instanceof Object); //true console.log(Foo instanceof Function); //true console.log(Foo instanceof Foo); //false |
- 其实 instanceof 的运行机制如下代码
1 2 3 4 5 6 7 8 9 10 11 | function instance_of(L, R) { //L 表示左表达式,R 表示右表达式 var O = R.prototype; // 取 R 的显示原型 L = L.__proto__; // 取 L 的隐式原型 while ( true ) { // 沿着原型链遍历 if (L === null ) return false ; if (O === L) // 这里重点:当 O 严格等于 L 时,返回 true return true ; L = L.__proto__; } } |
由上图可知 Foo.__proto__ !== Foo.prototype, so Foo instanceof Foo // false, String , Number 同理
typeof 运算符
native object: 本地对象(引用类型)
-- 独立于宿主环境的 ECMAScript 实现提供的对象
-- Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError, Global, Math, arguments
built-in object: 内置对象
-- 由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现, 开发者不必实例化.
-- Global, , Math,
-- 其中 Global 对象创建时也会创建 Object, window, document, String, Number, Array, Boolean, Date, 等对象
-- 其中进入函数EC时, 与创建AO, AO创建arguments 属性, 也就是Arguments对象
host object: 宿主对象
-- 所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象.
-- 所有BOM, DOM对象, 自定义对象
ECMA-262 规范:
-- 本地对象, 实例化了的对象都是 "object"
-- 本地对象或宿主对象, 未实例化的都是 "function"
-- 宿主对象, 实例化了的对象都不会是 "undefined" "boolean" "string" "number"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var n1 = 1; var n2 = Number(1); // Number 当做一个普通的函数, 返回 数值 var n3 = new Number(1); // Number 当作一个构造函数, 返回 数对象 var n4 = Object(1); // 类似2 var n5 = new Object(1); // 类似 4 var cat = new Cat(); // 自定义 console.log( typeof Number); // function console.log( typeof Object); // function console.log( typeof Function); // function console.log( typeof Cat); // function console.log( typeof n1); // number console.log( typeof n2); // number console.log( typeof n3); // object console.log( typeof n4); // object console.log( typeof n5); // object console.log( typeof cat); // object |
create 对象创建
对象字面量创建 : var a = {x:0 ,y:1}
new 创建 :
var a = new Object(); // 创建空对象 = {}
var a = new Array(); // 创建 空数组 = []
原型对象: .prototype
每个对象都有和另一个对象关联
如 new Date(); 就同时继承 Object.prototype 和 Data.prototype
Object.create(原型对象) 创建
1>. 定义: 创建一个可指定原型对象的并可以包含可选自定义属性的对象;
2> Object.create(proto [, properties]); 可选,用于配置新对象的属性;
1 2 3 4 5 6 7 8 9 10 11 12 | var o = Object.create({x:1 , y: 2}); // o继承了 属性 x ,y var o = Object.create(Object.prototype ); // 创建了一个空对象 function ProtoClass(){ this .a = 'ProtoClass' ; this .c = {}; this .b = function () { } } var obj1 = Object.create(ProtoClass.prototype, { foo:{value: 'obj1' , writable: true } }) |