/* * JavaScript 中,对象分为普通对象和函数对象,Object 和 Function 是JS自带的函数对象。 * 函数对象的一个属性是原型对象 prototype,普通对象没有prototype。 * 原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,它没有prototype属性)。 * 在函数对象创建的时候,创建了一个它的实例对象并赋值给它的prototype,即函数对象的原型对象就是自己的一个实例对象。(凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。Function.prototype = new Function (); 所以Function.prototype是函数对象。 * 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype,函数对象的原型对象的__proto__属性,它指向创建它的函数对象Object的原型对象prototype。Object.prototype对象也有__proto__属性,但它比较特殊,为null。这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。 * Object.__proto__ === Function.prototype * Object是函数对象,是通过new Function()创建,所以Object.__proto__指向Function.prototype。 * Function.__proto__ === Function.prototype * Function也是对象函数,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。 * Function.prototype.__proto__ === Object.prototype * 原型对象中都有个预定义的constructor属性,用来引用它的函数对象。 * Function.prototype.constructor === Function * Object.prototype.constructor === Object * Object.constructor===Function 本身Object就是Function函数构造出来的。 */ // 工厂模式创建对象 function createObject(name, age) { // 1.创建对象 var obj = new Object(); // 2.添加实例属性 obj.name = name; obj.age = age; // 3.添加实例方法 obj.run = function() { return this.name + this.age; }; return obj; } var myBox = createObject("Lee", 28); var myBoxBox = createObject("Leeee", 288); myBox.run(); // "Lee28" myBoxBox.run(); // "Leeee288" // 构造函数创建对象 function Box(name, age) { this.name = name; this.age = age; this.run = function() { return this.name + this.age; }; } var myBox = new Box("Lee", 28); myBox.run(); // "Lee28" // call() 对象冒充 定义对象o冒充Box 对象o便具备Box的属性和方法 var o = new Object(); Box.call(o, "Lee", 28); o.run(); // "Lee28" // 把构造函数中的方法通过全局函数来实现引用地址相等 但是破坏了封装性 不推荐 function Box(name, age) { this.name = name; this.age = age; this.run = run; } function run() { return this.name + this.age; } var myBox1 = new Box("Lee", 28); var myBox2 = new Box("Leeeee", 2888); myBox1.run(); // "Lee28" myBox2.run(); // "Leeeee2888" myBox1.run == myBox2.run; // true // 原型 // 定义(构造函数)对象 Box /* * 定义 Box 的默认操作 * 创建一个临时普通对象 即为 Box 的原型对象 var temp = new Box(); * 为 Box 定义属性 prototype 指向 Box 的原型对象 Box.prototype = temp; * 为 Box.prototype 对象定义 constructor 属性 该属性指向构造函数即 Box 对象 Box.prototype.constructor = Box; * 为 Box.prototype 对象定义 __proto__ 属性 该属性指向创建它的 Object 对象的原型对象 Box.prototype.__proto__ = Object.prototype; */ function Box(name, age) { this.name = name; this.age = age; this.run = function() { return this.name + this.age; } } typeof Box; // "function" typeof Box.prototype; // "object" Box.prototype; // Object {constructor: function Box(name, age), __proto__: Object} Box.prototype.__proto__ === Object.prototype; // true // 添加原型属性和方法 Box.prototype.name = "Lee"; Box.prototype.age = 28; Box.prototype.run = function() { return this.name + this.age; }; // 定义函数对象 Box 的实例对象 myBox1 /* * 定义函数对象 Box 的实例对象 myBox1 默认操作 * 为 myBox1 对象定义属性 __proto__ 该属性指向指向创建它的 Box 对象的原型对象 myBox1.__proto__ = Box.prototype; * JavaScript 会通过 __proto__ 属性向上寻找所有的属性和方法 所以为 Box.prototype 定义的属性和方法即为 Box 对象的所有实例对象共有属性和方法 */ var myBox1 = new Box("Leeeee", 288888); myBox1.__proto__; // Object {name: "Lee", age: 28, run: function(), constructor: function Box(name, age), __proto__: Object} myBox1.__proto__ === Box.prototype; // true myBox1.run(); // "Leeeee288888" myBox1.__proto__.run(); // "Lee28" myBox1.constructor; // 见下 /* function Box(name, age){ this.name = name; this.age = age; this.run = function() { return this.name + this.age; } } */ // 原型对象.isPrototypeOf(实例对象) 判定实例对象是不是指向了原型对象 Box.prototype.isPrototypeOf(myBox1); // true var myBox2 = new Object(); Box.prototype.isPrototypeOf(myBox2); // false // 删除实例属性 myBox1.name; // "Leeeee" delete myBox1.name; // true // myBox1.name 属性不存在 通过 __proto__ 属性寻找到 Box.prototype.name 属性 myBox1.name; // "Lee" // 判断实例属性中是否存在指定属性 myBox1.hasOwnProperty("name"); // false // 删除原型属性 delete Box.prototype.name; // true myBox1.name; // undefined // 判断实例属性或者原型属性中是否存在指定属性 "name" in myBox1; // false // 重定义或者覆盖属性 Box.prototype.name = "KK"; myBox1.name; // "KK" "name" in myBox1; // true // 遍历原型对象所有的属性和方法 var num = 0; for (o in Box.prototype) { console.log(o); // 依次输出见下 num++; } /* age run name */ // 遍历实例对象和原型对象所有的属性和方法 var num = 0; for (o in myBox1) { console.log(o + ":" + myBox1[o]); // 依次输出见下 num++; } /* age:288888 run:function () { return this.name + this.age; } name:KK */ // 使用字面量的方式创建原型对象 // 字面量创建原型对象 constructor 属性指向 Object Box.prototype = { name: "lucy", age: 10, run: function() { return this.name + this.age; } } var myBox = new Box("lucy", 12); myBox1.constructor; // 见下 /* function Box(name, age){ this.name = name; this.age = age; this.run = function() { return this.name + this.age; } } */ myBox.constructor; // function Object() { [native code] } myBox.constructor === Box; // false myBox.constructor === Object; // true // 使用字面量创建原型对象 并且把 constructor 指向 Box Box.prototype = { constructor: Box, name: "Lily", age: 10, run: function() { return this.name + this.age; } }; var myBox = new Box(); myBox.constructor; // 见下 /* function Box(name, age){ this.name = name; this.age = age; this.run = function() { return this.name + this.age; } } */ // Box.prototype 重写会切断实例与原来原型的关系 myBox1.__proto__.run(); // "KK28" myBox.__proto__.run(); // "Lily10" // 查看 Array 原型对象里的方法 sort() Array.prototype.sort; // function sort() { [native code] } // 给 String 原型对象添加方法 addString() String.prototype.addString; // undefined String.prototype.addString = function() { return this + "被添加了!"; } var box = "Lee"; box.addString(); // "Lee被添加了!" var box = new String("Lee"); box.addString(); // "Lee被添加了!" // 组合构造函数和原型模式 // 保持独立用的构造函数 function Box(name, age) { this.name = name; this.age = age; this.family = ['哥哥', '姐姐']; } // 保持共享用的原型 Box.prototype = { constructor: Box, run: function() { return this.name + this.age; } } var box = new Box("pu", 28); box.run(); // "pu28" // 动态原型模式,把原型封装到构造函数里 // 保持独立用的构造函数 function Box(name, age) { this.name = name; this.age = age; this.family = ['哥哥', '姐姐']; // 原型初始化只有第一次才执行 if (typeof this.run != 'function') { Box.prototype.run = function() { return this.name + this.age; }; } } var box = new Box("pu", 28); box.run(); // "pu28" // 寄生构造函数 = 工厂模式 + 构造函数 function Box(name, age) { var obj = new Object(); obj.name = name; obj.age = age; obj.run = function() { return this.name + this.age; }; return obj; } var box = new Box("Lee", 28); box.run(); // "Lee28" // 稳妥构造函数 = 工厂模式 function Box(name, age) { var obj = new Object(); obj.name = name; obj.age = age; obj.run = function() { return this.name + this.age; }; return obj; } // 不使用new var box = Box("Lee", 28); box.run(); // "Lee28 // 继承 // 通过原型链继承 function Box() { this.name = "Lee"; // L1 } Box.prototype.name = "Jack"; function Desk() { this.age = 100; } Desk.prototype = new Box(); var desk = new Desk(); // 属性寻找采用就近原则,如果L1行没有定义实例属性,打印出原型属性 Jack desk.name; // "Lee" // 使用对象冒充,只能继承构造里的实例属性,不能继承原型属性。 function Box(name, age) { this.name = name; this.age = age; this.family = ['哥哥', '姐姐', '妹妹']; } Box.prototype.run = function() { return this.name + this.age; } function Desk(name, age) { Box.call(this, name, age) } var desk = new Desk('Lee', 28); desk.name; // "Lee" desk.family; // ["哥哥", "姐姐", "妹妹"] // 出错因为继承不到原型里的方法 desk.run(); // desk.run is not a function(…) // 添加原型链继承 称为组合继承 Desk.prototype = new Box(); var desk = new Desk('Lee', 28); desk.run(); // "Lee28" // 原型式继承 // 临时中转函数 function obj(o) { function F() {} F.prototype = o; return new F(); } var box = { name: "Lee", age: 100, family: ['哥哥', '姐姐', '妹妹'] }; var myBox = obj(box); myBox.name; // "Lee" // 寄生式继承 = 原型式 + 工厂模式 目的是为了封装函数继承的过程 function obj(o) { function F() {} F.prototype = o; return new F(); } //寄生函数 function create(o) { var f = obj(o); f.run = function() { return this.name; }; return f; } var box = { name: "Lee", age: 100, family: ['哥哥', '姐姐', '妹妹'] }; var myBox = create(box); myBox.run(); // "Lee" // 寄生组合函数 // 临时中转函数 function obj(o) { function F() {} F.prototype = o; return new F(); } //寄生函数 function create(box, desk) { var f = obj(box.prototype); f.constructor = desk; //调整构造指针 desk.prototype = f; } function Box(name, age) { this.name = name; this.age = age; } Box.prototype.run = function() { return this.name + this.age; } function Desk(name, age) { Box.call(this, name, age); //对象冒充 } // 通过寄生组合继承来实现继承 create(Box, Desk);