1.JS對(duì)象創(chuàng)建的三種方式
1.第一種形式 工廠模型
function createPerson(name , sex , age){
var obj = new Object();
obj.name = name ;
obj.sex = sex ;
obj.age = age ;
obj.sayName = function(){
alert(this.name);
}
return obj;
}
var p1 = createPerson('z3' , '男' , 20);
var p2 = createPerson('z4' , '女' , 25);
//alert(p1.sex);
p1.sayName();
```
2. 構(gòu)造函數(shù)式, 例如new Array new Date
// 函數(shù)的第一個(gè)字母大寫 (類的模版)
function Person(name , age , sex){
this.name = name ;
this.age = age ;
this.sex = sex ;
this.sayName = function(){
alert(this.name);
}
}
//構(gòu)造一個(gè)對(duì)象 new關(guān)鍵字 傳遞參數(shù) 執(zhí)行模版代碼 返回對(duì)象
var p1 = new Person('小1' , 20 , '男');
var p2 = new Person('小2' , 21 , '女');
//alert(p1.name);
//p1.sayName();
//alert(p1 == p2); //類的概念:根據(jù)模版創(chuàng)建出不同的實(shí)例對(duì)象
//alert(p1.constructor == Person);
//alert(p2.constructor == Person);
alert(p1 instanceof Person);
alert(p1 instanceof Object);
3.創(chuàng)建對(duì)象的方式:
// 1當(dāng)作構(gòu)造函數(shù)去使用 :
var p1 = new Person('小1' , 20 , '男');
// 2作為普通的函數(shù)去調(diào)用
Person('小2' , 25 , '男'); //在全局環(huán)境里定義屬性并復(fù)制 直接定義在window上
//alert(name); // 小2
// 3在另一個(gè)對(duì)象的作用域中調(diào)用
var o = new Object();
// call applly
Person.call(o,'小4' , 12 , '女');
alert(o.name); // 小4
#####2.原型 prototype
原型:原型對(duì)象里的所有屬性和方法被所有構(gòu)造函數(shù)實(shí)例化出來的對(duì)象所共享
//使用構(gòu)造函數(shù)方式創(chuàng)建對(duì)象
function Person(name , age){
this.name = name ;
this.age = age ;
this.sayName = function(){alert('我是姓名!')};
}
var p1 = new Person('z3',20);
var p2 = new Person('z4',21);
p1.sayName(); // 我是姓名!
p2.sayName(); // 我是姓名!
alert(p1.sayName == p2.sayName); // false 每次創(chuàng)建對(duì)象都新建一個(gè)函數(shù)(實(shí)例化一個(gè)方法)
alert(p1.prototype == p2.prototype); // true
可以這樣避免每次創(chuàng)建對(duì)象都新建一個(gè)函數(shù)(實(shí)例化一個(gè)方法)
function Person(name , age){
this.name = name ;
this.age = age ;
this.sayName = sayName ;
}
// 定義了一次函數(shù)
function sayName(){
alert(this.name);
}
var p1 = new Person('z3',20);
var p2 = new Person('z4',21);
p1.sayName(); // z3
p2.sayName(); // z4
alert(p1.sayName == p2.sayName); // true
alert(p1.name == p2.name); // false
> prototype 創(chuàng)建每一個(gè)函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性其實(shí)是一個(gè)指針,而這個(gè)指針總指向一個(gè)對(duì)象
這個(gè)對(duì)象的用途就是將特定的屬性和方法包含在內(nèi),起到一個(gè)所有實(shí)例所共享的作用
利用原型對(duì)象避免 使用構(gòu)造行數(shù)創(chuàng)建對(duì)象時(shí)沒次都新建一個(gè)函數(shù)
function Person(){
}
var obj = Person.prototype;
alert(typeof obj); //object
// 公共屬性 name age sayName
obj.name = 'z3';
obj.age = 20 ;
obj.sayName = function(){alert(this.name);};
var p1 = new Person();
var p2 = new Person();
alert(p1.age); // 20
alert(p2.age); // 20
p1.sayName(); // z3
p2.sayName(); // z3
alert(p1.sayName == p2.sayName); // true 可見對(duì)象是使用一個(gè)函數(shù)創(chuàng)建而來
p1.name = 'lisi';
alert(p2.name); // z3 不影響對(duì)象
關(guān)于JS原型, 每一個(gè)對(duì)象都有prototype屬性,該屬性指向原型對(duì)象。 每個(gè)對(duì)象共享原型對(duì)象中的屬性

構(gòu)造函數(shù) 原型對(duì)象 實(shí)例對(duì)象的關(guān)系
構(gòu)造函數(shù).prototype = 原型對(duì)象
原型對(duì)象.constructor = 構(gòu)造函數(shù)
實(shí)例對(duì)象.prototype = 原型對(duì)象
>isPrototypeOf(); 判斷原型的方法
>Object.getPrototypeOf() 根據(jù)實(shí)例對(duì)象獲得原型對(duì)象
每次當(dāng)代碼讀取一個(gè)對(duì)象的屬性的時(shí)候,首先會(huì)進(jìn)行一次搜索,搜索實(shí)例對(duì)象里name的屬性,看看有沒有。如果沒有,再去實(shí)例所對(duì)應(yīng)的原型對(duì)象里去搜索name屬性,如果有則返回, 沒有返回undefined
>hasOwnProperty(屬性名) 判斷一個(gè)對(duì)象的屬性是屬于原型屬性 還是屬于實(shí)例屬性
function Person(){
}
// 添加原型屬性
Person.prototype.name = 'z3';
Person.prototype.age = 20;
Person.prototype.sayName = function () {
alert('我是原型對(duì)象的方法');
};
// 用Person實(shí)例化的所有對(duì)象都共享屬性 name age sayName
var p3 = new Person();
alert(p3.hasOwnProperty('name')); // false
>in 操作符 判斷屬性是否存在于實(shí)例對(duì)象或原型對(duì)象中 如果其中一個(gè)存在就返回true
var p1 = new Person();
alert('name' in p1); // true
var p2 = new Person();
p2.name = 'w3';
alert('name' in p2); // true
所以 判斷一個(gè)屬性是否存在原型中,可以使用 hasOwnProperty和in結(jié)合判斷
// object:當(dāng)前對(duì)象 name:要判斷的屬性
function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && name in object;
}
> Object.keys() 拿到當(dāng)前對(duì)象里的所有keys 返回一個(gè)數(shù)組(不可被枚舉的constructor屬性除外)
> Object.getOwnPropertyNames() 枚舉對(duì)象所有的屬性,不管該內(nèi)部屬性能否被枚舉
var p1 = new Person();
p1.name = 'z3';
p1.age = 20;
var attributes = Object.keys(p1);
alert(attributes); //name,age
var attributes2 = Object.keys(Person.prototype);
alert(attributes2); //name,age,sayName 原型對(duì)象的屬性數(shù)組
// constructor屬性, 該屬性是不能被枚舉的,[eable = false]
// Object.getOwnPropertyNames() 枚舉對(duì)象所有的屬性,不管該內(nèi)部屬性能否被枚舉
var attributes3 = Object.getOwnPropertyNames(Person.prototype);
alert(attributes3); //constructor,name,age,sayName
> 使用原型擴(kuò)展對(duì)象中的屬性和方法
例如:模擬Array中的each循環(huán)方法
Array中的forEach方法,只適合遍歷一維數(shù)組, 使用原型擴(kuò)展一個(gè)each方法,完成遍歷多維數(shù)組
var arr = [1,2,3,[4,[5]]];
arr.forEach(function (item, index, array) {
alert(item); // 1 2 3 4,5
});
使用原型對(duì)象實(shí)現(xiàn)一個(gè)Array的each方法,遍歷多維數(shù)組
var arr = [1,2,3,[4,[5,[6]]]];
// 在原型對(duì)象上添加方法
Array.prototype.each = function (fn) {
try{
// 目的:遍歷數(shù)組的每一項(xiàng)
this.i || (this.i = 0); // 計(jì)數(shù)器,記錄當(dāng)前元素的位置 沒有使用var i= 0; 是為了避免變量沖突
// 嚴(yán)謹(jǐn)?shù)呐袛啵菏裁磿r(shí)候走each核心方法
// 當(dāng)數(shù)組的長(zhǎng)度大于0時(shí)并且傳遞的參數(shù)必須為函數(shù)fn.constructor == Function
if(this.length > 0 && fn.constructor == Function) {
// 循環(huán)遍歷數(shù)組的每一項(xiàng)
while(this.i < this.length){ // 底層代碼少寫forin循環(huán) 避免變量沖突
// 核心代碼, 獲取數(shù)組的每一項(xiàng)
var e = this[this.i];
// 如果當(dāng)前元素獲取到了 并且是一個(gè)數(shù)組
if(e && e.constructor == Array){
//直接做遞歸操作
e.each(fn);
} else {
// 如果不是數(shù)組(單個(gè)的元素)
//fn(e);
//var obj = true;
//fn.apply(obj, [e]);
//這的目的就是為了把數(shù)組的當(dāng)前元素傳遞給fn函數(shù) 并讓函數(shù)執(zhí)行
//fn.apply(e, [e]);
fn.call(e,[e]);
}
this.i++;
}
this.i == null; // 釋放內(nèi)存 垃圾回收機(jī)制回收變量
}
} catch(ex){
// do something
}
return this; // this 指向方法的調(diào)用者
}
// 運(yùn)行
arr.each(function (item) {
alert(item); // 1 2 3 4 5 6
});
>簡(jiǎn)單原型
直接通過對(duì)象字面量來重寫整個(gè)原型對(duì)象(這種方法會(huì)改變?cè)蛯?duì)象的構(gòu)造器)
function Person(){
}
Person.prototype = { // 給原型對(duì)象重新設(shè)置了一個(gè)匿名方法,原型對(duì)象的構(gòu)造器不再是Person(){};
// 需要給原型對(duì)象添加構(gòu)造器
// constructor:Person, // 將構(gòu)造器屬性直接寫在這里,這樣不行,以為constructor是不可枚舉的,在這里可以枚舉得到
name:'z3',
age:20,
job:'程序員',
say: function () {
alert('我是原型的函數(shù)!');
}
};
// 使用Object.defineProperty()方法給原型對(duì)象重新設(shè)置構(gòu)造器
// 參數(shù)1: 重設(shè)構(gòu)造器的對(duì)象 參數(shù)2:設(shè)置什么屬性 參數(shù)3:option配置項(xiàng)
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value:Person
});
原型具有動(dòng)態(tài)性,在普通原型中可以先創(chuàng)建對(duì)象,再添加原型屬性,例如:
function Person(){
}
var p1 = new Person();
// 原型對(duì)象的構(gòu)造器默認(rèn)為Person
Person.prototype.say = function () {
alert('我是方法!')
};
p1.say(); // 我是方法!
但是使用簡(jiǎn)單原型時(shí),是不可以先創(chuàng)建對(duì)象后添加原型屬性的
function Person(){
}
var p1 = new Person(); // 原型對(duì)象是空的
Person.prototype = { // 給原型對(duì)象重新設(shè)置了一個(gè)匿名方法,原型對(duì)象的構(gòu)造器不再是Person(){};
say: function () {
alert('我是原型的函數(shù)!');
}
};
Object.defineProperty(Person.prototype, 'constructor', {
enumerable:false,
value: Person
});
p1.say(); //爆錯(cuò): p1.say is not a function(…) 因?yàn)樵蛯?duì)象里面沒有任何屬性和方法
// 注意:簡(jiǎn)單原型的使用順序(實(shí)例對(duì)象必須在原型對(duì)象之后創(chuàng)建)
var p2 = new Person();
p2.say(); // 我是原型的函數(shù)!
>原型的弊端和創(chuàng)建對(duì)象的方式
原型里的屬性和方法被所有對(duì)象所共享,修改一個(gè)對(duì)象的屬性,兩個(gè)一個(gè)對(duì)象的屬性也變了
function Person(){
}
Person.prototype = {
name:'z3',
age:20,
job:'程序員',
friends:['李四', '王五'],
sayName: function () {
alert('我的名字!')
}
};
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value:Person
})
var p1 = new Person();
var p2 = new Person();
p1.friends.push('趙六');
alert(p2.friends); // 李四,王五,趙六
// 原型里的屬性和方法被所有對(duì)象所共享, static的
創(chuàng)建對(duì)象的方式:
1.組合使用原型和構(gòu)造函數(shù)式 (定義一個(gè)類,開發(fā)常用的方法)
function Person(name, age, friends, job) {
this.name = name;
this.age = age;
this.friends = friends;
this.job = job;
}
Person.prototype = {
sayName: function () {
alert(this.name);
}
};
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value:Person
})
var p1 = new Person('z3', 20, ['w5', 'z6'], '技術(shù)總監(jiān)');
var p2 = new Person('l4', 18, ['w5', 'z6'], 'boss');
p1.friends.push('趙六');
alert(p1.friends); // w5, z6, 趙六
alert(p2.friends); // w5, z6
2. 動(dòng)態(tài)原型模式(讓屬性和方法 都封裝到一起)
function Person(name, age, friends, job) {
this.name = name;
this.age = age;
this.friends = friends;
this.job = job;
// 動(dòng)態(tài)原型方法
if(typeof this.sayName != 'function'){
Person.prototype.sayName = function () {
alert(this.name);
}
}
}
3.穩(wěn)妥構(gòu)造函數(shù)式:durable object(穩(wěn)妥對(duì)象) 在非常安全的環(huán)境中
特點(diǎn):1.沒有公共屬性 2.不能使用this對(duì)象
function Person(name, age, job){
// 創(chuàng)建一個(gè)要返回的對(duì)象
var obj = new Object();
// 可以定義一些私有的變量和函數(shù)
var sex = '男';
var saySex = function () {};
var name = name;
// 添加對(duì)外方法
obj.sayName = function () {
alert(name);
}
return obj;
}
var p1 = new Person('z3',20,'程序員');
p1.sayName(); // 只能通過方法訪問名字
#####3.繼承
######JS采用原型鏈的概念實(shí)現(xiàn)繼承
構(gòu)造函數(shù)、原型對(duì)象和實(shí)例對(duì)象三者之間的關(guān)系有:
1. 構(gòu)造函數(shù).prototype = 原型對(duì)象
2. 原型對(duì)象.constructor = 構(gòu)造函數(shù)(模版)
3. 原型對(duì)象.isPrototypeOf(實(shí)例對(duì)象),判斷實(shí)例對(duì)象的原型 是不是當(dāng)前對(duì)象
4. 構(gòu)造函數(shù)與實(shí)例對(duì)象是類和實(shí)例的關(guān)系
根據(jù)它們的關(guān)系實(shí)現(xiàn)JS的繼承
// 父類構(gòu)造函數(shù)
function Sup(name){
this.name = name;
}
// 父類原型對(duì)象
Sup.prototype = {
sayName: function () {
alert(this.name);
}
}
Object.defineProperty(Sup.prototype, 'constructor', {
enumerable:false,
value: Sup
});
//子類構(gòu)造函數(shù)
function Sub(age){
this.age = age;
}
// 讓子類的原型對(duì)象等父類的實(shí)例,結(jié)果會(huì)怎么樣? 實(shí)現(xiàn)JS的繼承
// 1.此時(shí)的原型對(duì)象包含一個(gè)指向另一個(gè)原型的指針
// sup的實(shí)例對(duì)象 和 sup的原型對(duì)象 有一個(gè)關(guān)系
// 2.相應(yīng)的另一個(gè)原型中也包含了一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針
// 子類的原型對(duì)象的構(gòu)造器變成了父類的構(gòu)造器
alert(Sub.prototype.constructor); // 結(jié)果 function Sub(age){this.age = age;}
Sub.prototype = new Sup('z3');
alert(Sub.prototype.constructor); // 結(jié)果 function Sup(name){this.name = name;}
var sub1 = new Sub(19);
alert(sub1.name); // z3
alert(sub1.age); // 19
sub1.sayName(); // z3
sub1.name = 'lisi';
sub1.sayName(); // lisi
######繼承的方式
1.原型繼承 即繼承了父類的模板也繼承了父類的原型對(duì)象
// 父類
function Person(name, age){
this.name = name;
this.age = age;
}
// 父類的原型對(duì)象屬性
Person.prototype.id = 10;
//子類
function Boy(sex){
this.sex = sex;
}
// 繼承已經(jīng)實(shí)現(xiàn)了
Boy.prototype = new Person('z3');
var b = new Boy();
alert(b.name); // z3
alert(b.id); // 10
2.類繼承 只繼承模板不繼承原型對(duì)象(借用構(gòu)造函數(shù)方式繼承)
function Person(name, age){
this.name = name;
this.age = age;
}
// 父類的原型對(duì)象屬性
Person.prototype.id = 10;
//子類
function Boy(name, age, sex){
// call apply
Person.call(this, name, age);
this.sex = sex;
}
var b = new Boy('z3', 20, '男');
alert(b.name); // z3
alert(b.age); // 20
alert(b.sex); // 男
alert(b.id); // undefined 父類的原型對(duì)象并沒有繼承
3.原型繼承+借用構(gòu)造函數(shù) = 混合繼承
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.id = 10;
Person.prototype.sayName = function(){alert(this.name);};
function Boy(name, age, sex){
// call apply
Person.call(this, name, age); // 1.借用構(gòu)造函數(shù)繼承,復(fù)制了父類的模板
this.sex = sex;
}
// 2.原型繼承
Boy.prototype = new Person(); //繼承父類的原型對(duì)象和復(fù)制了父類的模板
//存在的缺點(diǎn)就是復(fù)制了兩次父類的模板
4.混合繼承
function extend(sub ,sup){
// 目的: 實(shí)現(xiàn)只繼承父類的原型對(duì)象
var F = new Function(); // 1 創(chuàng)建一個(gè)空函數(shù) 目的:空函數(shù)進(jìn)行中轉(zhuǎn)
F.prototype = sup.prototype; // 2 實(shí)現(xiàn)空函數(shù)的原型對(duì)象和超類的原型對(duì)象轉(zhuǎn)換
sub.prototype = new F(); // 3 原型繼承
sub.prototype.constructor = sub ; // 4還原子類的構(gòu)造器
//保存一下父類的原型對(duì)象: 一方面方便解耦 另一方面方便獲得父類的原型對(duì)象
sub.superClass = sup.prototype; //自定義一個(gè)子類的靜態(tài)屬性 接受父類的原型對(duì)象
//判斷父類的原型對(duì)象的構(gòu)造器 (加保險(xiǎn))
if(sup.prototype.constructor == Object.prototype.constructor){
sup.prototype.constructor = sup ; //手動(dòng)歡迎父類原型對(duì)象的構(gòu)造器
}
}
// 混合繼承:原型繼承和借用構(gòu)造函數(shù)繼承
function Person( name , age){
this.name = name ;
this.age = age ;
}
Person.prototype = {
constructor: Person ,
sayHello: function(){
alert('hello world!');
}
};
function Boy(name , age , sex){
//call 綁定父類的模版函數(shù) 實(shí)現(xiàn) 借用構(gòu)造函數(shù)繼承 只復(fù)制了父類的模版
Boy.superClass.constructor.call(this , name , age);
this.sex = sex ;
}
//原型繼承的方式: 即繼承了父類的模版 又繼承了父類的原型對(duì)象
//Boy.prototype = new Person();
// 只繼承一遍父類的原型對(duì)象
extend(Boy , Person);
// 給子類加了一個(gè) 原型對(duì)象的方法
Boy.prototype.sayHello = function(){
alert('hi javascript!');
}
var b = new Boy('張三' , 20 , '男');
alert(b.name);
alert(b.sex);
//b.sayHello();
Boy.superClass.sayHello.call(b);
//alert(Boy.superClass.constructor);
// 混合繼承的缺點(diǎn): 3件事 : 繼承了父類的2次模版 , 繼承了一次父類的原型對(duì)象
// extend方法 2件事: 繼承1次父類的模版 繼承一次父類的原型對(duì)象
參考白賀翔教程