一、面向?qū)ο蠡咎卣?/h4>
- 封裝:也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
- 繼承:通過繼承創(chuàng)建的新類稱為“子類”或“派生類”。繼承的過程,就是從一般到特殊的過程。
- 多態(tài):對象的多功能,多方法,一個方法多種表現(xiàn)形式。
- Javascript是一種基于對象(object-based)的語言。但是,它又不是一種真正的面向?qū)ο缶幊蹋∣OP)語言,因為它的語法中沒有class(類)—–es6以前是這樣的。所以es5只有使用函數(shù)模擬的面向?qū)ο蟆?/li>
二、對象實例化方式
- 原始模式:這樣的寫法有兩個缺點,一是如果多生成幾個(100個!)實例,寫起來就非常麻煩;二是實例與原型之間,沒有任何辦法,可以看出沒有什么聯(lián)系。
var Car = {
color: 'red',//車的顏色
wheel: 4,//車輪數(shù)量
}
var Car2 = {
color: 'blue',
wheel: 4,
}
alert(Car.color);//red
- 原始模式的改進:通過寫一個函數(shù),解決代碼重復(fù)的問題。
function createCar(color,wheel) {
return {
color:color,
wheel:wheel
}
}
//然后生成實例對象,就等于是在調(diào)用函數(shù):
var cat1 = createCar("紅色","4");
var cat2 = createCar("藍色","4");
alert(cat1.color);//紅色
- 工廠模式
function createCar(color,wheel){//createCar工廠
var obj = new Object;//或obj = {} 原材料階段
obj.color = color;//加工
obj.wheel = wheel;//加工
return obj;//輸出產(chǎn)品
}
//實例化
var cat1 = createCar("紅色","4");
var cat2 = createCar("藍色","4");
alert(cat1.color);//紅色
- 構(gòu)造函數(shù)模式:為了解決從原型對象生成實例的問題,Javascript提供了一個構(gòu)造函數(shù)(Constructor)模式。 所謂”構(gòu)造函數(shù)”,其實就是一個普通函數(shù),但是內(nèi)部使用了this變量。對構(gòu)造函數(shù)使用new運算符,就能生成實例,并且this變量會綁定在實例對象上。加
new執(zhí)行的函數(shù)構(gòu)造內(nèi)部變化:自動生成一個對象,this指向這個新創(chuàng)建的對象,函數(shù)自動返回這個新創(chuàng)建的對象
function CreateCar(color,wheel){//構(gòu)造函數(shù)首字母大寫
//不需要自己創(chuàng)建對象了
this.color = color;//添加屬性,this指向構(gòu)造函數(shù)的實例對象
this.wheel = wheel;//添加屬性
//不需要自己return了
}
//實例化
var cat1 = new CreateCar("紅色","4");
var cat2 = new CreateCar("藍色","4");
alert(cat1.color);//紅色
三、構(gòu)造函數(shù)注意事項
- 此時CreateCar稱之為構(gòu)造函數(shù),也可以稱之類,構(gòu)造函數(shù)就是類 。
- cat1,cat2均為CreateCar的實例對象。
- CreateCar構(gòu)造函數(shù)中this指向CreateCar實例對象即
new CreateCar( )出來的對象。
- 必須帶new 。
- 構(gòu)造函數(shù)首字母大寫,這是規(guī)范,官方都遵循這一個規(guī)范,如Number() Array()。
- contructor:這時cat1和cat2會自動含有一個constructor屬性,指向它們的構(gòu)造函數(shù),即CreateCar。
alert(cat1.constructor == CreateCar); //true
alert(cat2.constructor == CreateCar); //true
- 每定義一個函數(shù),這個函數(shù)就有一個 prototype 的屬性{},
__proto__ 指向被實例化的構(gòu)造函數(shù)的prototype,prototype默認帶constructor屬性,constructor指向構(gòu)造函數(shù)。
- instanceof 運算符:
object instanceof constructor運算符,驗證構(gòu)造函數(shù)與實例對象之間的關(guān)系。
alert(cat1 instanceof CreateCar ); //true
alert(cat2 instanceof CreateCar ); //true
四、構(gòu)造函數(shù)的問題
var Car = {
color: 'red',//車的顏色
wheel: 4,//車輪數(shù)量
}
var Car2 = {
color: 'blue',
wheel: 4,
}
alert(Car.color);//red
function createCar(color,wheel) {
return {
color:color,
wheel:wheel
}
}
//然后生成實例對象,就等于是在調(diào)用函數(shù):
var cat1 = createCar("紅色","4");
var cat2 = createCar("藍色","4");
alert(cat1.color);//紅色
function createCar(color,wheel){//createCar工廠
var obj = new Object;//或obj = {} 原材料階段
obj.color = color;//加工
obj.wheel = wheel;//加工
return obj;//輸出產(chǎn)品
}
//實例化
var cat1 = createCar("紅色","4");
var cat2 = createCar("藍色","4");
alert(cat1.color);//紅色
new執(zhí)行的函數(shù)構(gòu)造內(nèi)部變化:自動生成一個對象,this指向這個新創(chuàng)建的對象,函數(shù)自動返回這個新創(chuàng)建的對象function CreateCar(color,wheel){//構(gòu)造函數(shù)首字母大寫
//不需要自己創(chuàng)建對象了
this.color = color;//添加屬性,this指向構(gòu)造函數(shù)的實例對象
this.wheel = wheel;//添加屬性
//不需要自己return了
}
//實例化
var cat1 = new CreateCar("紅色","4");
var cat2 = new CreateCar("藍色","4");
alert(cat1.color);//紅色
new CreateCar( )出來的對象。alert(cat1.constructor == CreateCar); //true
alert(cat2.constructor == CreateCar); //true
__proto__ 指向被實例化的構(gòu)造函數(shù)的prototype,prototype默認帶constructor屬性,constructor指向構(gòu)造函數(shù)。object instanceof constructor運算符,驗證構(gòu)造函數(shù)與實例對象之間的關(guān)系。alert(cat1 instanceof CreateCar ); //true
alert(cat2 instanceof CreateCar ); //true
構(gòu)造函數(shù)方法很好用,但是存在一個浪費內(nèi)存的問題。如果現(xiàn)在為其再添加一個方法showWheel。那么,CreateCar就變成了下面這樣,這樣做有一個很大的弊端,對于每一個實例對象,showWheel都是一模一樣的內(nèi)容,每一次生成一個實例,都必須生成重復(fù)的內(nèi)容,多占用一些內(nèi)存。這樣既不環(huán)保,也缺乏效率。
function CreateCar(color,wheel){
this.color = color;
this.wheel = wheel;
this.showWheel = function(){//添加一個新方法
alert(this.wheel);
}
}
//還是采用同樣的方法,生成實例:
var cat1 = new CreateCar("紅色","4");
var cat2 = new CreateCar("藍色","4");
alert(cat1.showWheel == cat2.showWheel); //false
五、Prototype 原型
Javascript規(guī)定,每一個構(gòu)造函數(shù)都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)的實例繼承。 這意味著,我們可以把那些不變的屬性和方法,直接定義在prototype對象上。__proto__是原型鏈,指向?qū)嵗暮瘮?shù)原型。
function CreateCar(color,wheel){
//屬性寫構(gòu)造函數(shù)里面
this.color = color;
this.wheel = wheel;
}
//方法寫原型里面
CreateCar.prototype.showWheel = function(){
alert(this.wheel);
}
CreateCar.prototype.showName = function(){
alert('車');
}
//生成實例。
var cat1 = new CreateCar("紅色","4");
var cat2 = new CreateCar("藍色","4");
cat1.showName();//'車'
//這時所有實例的showWheel屬性和showName方法,其實都是同一個內(nèi)存地址,指向prototype對象,因此就提高了運行效率。
alert(cat1.showWheel == cat2.showWheel );//true
alert(cat1.showName == cat2.showName );//true
console.log(cat1.__proto__ === CreateCar.prototype); //true
六、對象和函數(shù)的關(guān)系
對象是由函數(shù)構(gòu)造出來的。
- Object是Function 的一個實例。
Object.constructor == Function //true
- 函數(shù)是Function 的實例,但不是Object 的實例。
function fn(){}
fn.constructor == Function //true
fn.constructor == Object //false
- {} 與 Object 的關(guān)系。
var obj = {};
obj.constructor === Object //true
七、靜態(tài)方法和靜態(tài)屬性
只屬于類而不屬于實例化對象
function foo(){
this.show = function(){
return this;
}
}
foo.test = 123; //靜態(tài)屬性
foo.say = function(){
return this;
}
foo.say();
var fn = new foo(); //實例化的新的對象,this指向這個新的對象,不能訪問類的靜態(tài)方法
fn.say(); //Noname1.html:45 Uncaught TypeError: fn.say is not a function
console.log(foo.say() == fn.say());
八、對象繼承
- 利用
call()及for in繼承 。
給對象的constructor.prototype添加方法屬性,對象就會繼承,如果要實現(xiàn)一個對象繼承其他對象,采用如下方法。
//人類
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.run = function(){
console.log('跑路~')
};
Person.prototype.say = function(){
console.log('說話~')
};
console.log(Person.prototype);
//男人
function Man(){
this.sex = "男";
}
Man.prototype = Person.prototype;
Man.prototype.yyy = function(){
console.log('嚶嚶嚶');
}
//會發(fā)現(xiàn)Person的prototype也改變了,因為復(fù)雜對象的賦值操作是引用而不是賦值
console.log(Person.prototype);
//人類
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.run = function(){
console.log('跑路~')
};
Person.prototype.say = function(){
console.log('說話~')
};
console.log(Person.prototype);
//男人
function Man(){
this.sex = "男";
}
for(var key in Person.prototype){
Man.prototype[key] = Person.prototype[key];
console.log(key)
}
Man.prototype.yyy = function(){
console.log('嚶嚶嚶');
}
console.log(Person.prototype);
var xm = new Man();
xm.yyy();
- 采用中介
function ClassA(name){
this.name = name;
}
ClassA.prototype.say = function(){
console.log(666);
}
//中繼來做準備工作
function Ready(){}//
Ready.prototype = ClassA.prototype;//引用
//需要來繼承ClassA
function ClassB(){}
ClassB.prototype = new Ready();//new 返回了一個新對象 __proto__指向被實例化的構(gòu)造函數(shù)的prototype
ClassB.prototype.constructor = ClassB;
console.log(ClassB.prototype);
- 采用中介,使用
call改變this指向
function ClassA(name){
this.name = name;
}
ClassA.prototype.showName = function(){
console.log(this.name);
}
//中繼來做準備工作
function Ready(){}//
Ready.prototype = ClassA.prototype;//引用
//需要來繼承ClassA
function ClassB(name){
ClassA.call(this,name);
}
ClassB.prototype = new Ready();//new 返回了一個新對象 __proto__指向被實例化的構(gòu)造函數(shù)的prototype
ClassB.prototype.constructor = ClassB;
console.log(ClassB.prototype);
var xiaoming = new ClassB('小明');
xiaoming.showName();
九、多態(tài)
同一個方法,面對不同的對象有不同的表現(xiàn)形式就叫做多態(tài)。
var obj = {
eat : function(_type){
if(_type == '貓'){
console.log('貓糧')
}else if (_type == "狗") {
console.log('狗糧')
}else{
console.log("吃飯");
}
}
};
obj.eat("狗");
十、hasOwnProperty
查看該屬性是否在這個對象本身上,只有在自身屬性上才會返回真,在原型鏈上會返回假。
function ClassA(){}
ClassA.prototype.test = function(){
console.log('test')
}
var a = new ClassA();
a.test();
console.log(a.hasOwnProperty('test')); //false
十一、描述符(修飾符)
描述符是對一個屬性的特性的描述,defineProperty設(shè)置描述符(修飾符),value設(shè)置屬性值,configurable是否允許修飾符被改變 默認為false,enumerable 是否可以被枚舉 默認為false,writable 是否可以被 = 等號改變 默認為false。
var obj = {
a : 1
};
var c = 666;
Object.defineProperty(obj,'c',{
//value : 233,
//enumerable : false,
//writable : true,//他的值能否改變
//設(shè)置的時候調(diào)用
set : function(n){
//n 就是等號的右邊的值
c = c*n;
},
//獲取的時候調(diào)用
get : function(){
return c;
},
configurable : true,//是否可以再次修改修飾符
});