前端筆記之JavaScript物件導向(二)內建建構函式&相關方法|屬性|運算子&繼承&物件導向

mufengsm發表於2019-04-18

一、複習

1.1複習上下文

函式的呼叫方式

上下文

fun()

window

obj.fun()

obj

box.onclick = fun

box

setInterval(fun,1000)

setTimeout(fun,1000)

window

array[8]()

array

new fun()

祕密建立的新物件

 要看清楚最終的函式呼叫者是誰。

IIFE也被當做函式直接執行,IIFEthis都是window物件

函式的arguments是類陣列物件,比如傳入的第0項引數是函式,讓它執行:arguments[0](),函式中的上下文是arguments物件。還要知道函式的lengtharguments.length的區別。


 

1.2建構函式

當一個函式用new運算子呼叫時,此時這個函式叫“建構函式”(constructor

建構函式四步走:

建立一個新空物件
this繫結給這個空物件
執行語句
返回這個物件

new 出來的所有物件擁有相同屬性群,所以可以把建構函式當做類,被new出來的物件都是這個函式的例項。

 

當建構函式裡面有return語句時,注意:

如果是return基本型別值,無視這個return,還是返回祕密建立的物件

如果是return引用型別值,則不返回原來的物件,而是返回引用型別值

 

原型鏈的知識

建構函式的prototype屬性就是例項的__proto__屬性。對於一個物件來說,__proto__這個屬性叫做自己的原型物件,就是自己的原型鏈。這個原型鏈查詢功能,obj.haha時,obj身上沒有haha屬性,此時會查詢obj.__proto__身上有沒有haha屬性,並且會繼續查詢obj.__proto__.__proto__屬性身上有沒有haha屬性……

 


 

二、原型鏈

 Object.prototype是所有物件原型鏈的終點

任何物件都有原型物件(__proto__),最終指向Object.prototype,但是Object.prototype很特殊,它的__proto__終點是null

JS中,物件是物件、函式、陣列、正則等是物件,所有引用型別值都是物件,它們都有__proto__屬性。

甚至,xiaoming__proto__也是一個物件,這個物件也有__proto__

var obj = {}

console.log(obj.__proto__ === Object.prototype); //true

 

Object()是內建的建構函式,所有的物件,可以認為是Object new出來的

var obj = new Object();
obj.name = "小明";
obj.age = 12;
console.log(obj)
console.log(obj.__proto__ === Object.prototype);

“{}”物件的__proto__都指向Object.prototype,因為它都是Object new出來的。

 

function People(name,age){
   this.name = name;
   this.age = age;
}
var xiaoming = new People("小明",12);
console.log(xiaoming.__proto__ === People.prototype); //true
console.log(xiaoming.__proto__.__proto__ === Object.prototype); //true
console.log(xiaoming.__proto__.__proto__.__proto__);; //null

 

綜上所述,xiaoming的原型People.prototype也有原型,小明完整的家譜:

 

Object.prototype是唯一一個沒有__proto__的物件,其他所有物件、函式、陣列、正則等都有__proto__


三、內建建構函式

JS內建了很多建構函式,它們也稱為“基本型別值”、“引用型別值”的包裝類。

3.1引用型別值的建構函式

引用型別值的建構函式:Object()Function()Array()RegExp()

3.1.1 Object()函式

Object()是內建的建構函式,可以直接 new它,返回一個空物件,可以給這個空物件新增屬性:

var obj = new Object();
obj.name = "小明";
obj.age = 12;
console.log(obj)
console.log(obj.__proto__ === Object.prototype);

等價於:

var obj = {
name:"小明",
age:12
}

3.1.2 Function()函式

所有function字面量都是它的例項

function sum(a,b){
   alert(a+b);
}
sum(3,5);

等價於:

var sum = new Function("a","b","alert(a+b);alert('算完啦!')");
sum(4,5)

 

new Function的時候,先羅列所有形參列表,最後一個引數是函式體,注意,引數都是字串。

console.log(sum.__proto__ === Function.prototype)

 

任何函式都是Function()建構函式的例項,Object也是Function的例項,Function自己也是自己的例項。Function自己new自己。

console.log(Object.__proto__ === Function.prototype);   //true
console.log(Function.__proto__ === Function.prototype); //true

 

FunctionObject的關係:

console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Function.__proto__.__proto__ === Object.prototype);
console.log(Function.__proto__.__proto__ === Object.__proto__.__proto__);
console.log(Function.__proto__ === Object.__proto__);


3.1.3 Array()函式

Array()是系統內建的陣列建構函式,任何的陣列都是Array() new出來的。

var arr = new Array();
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
console.log(arr);

 

等價於:

var arr = [100,200,300];

 

函式能填引數,表示陣列長度,但陣列還是空陣列:

var arr = new Array(8);

常用的陣列方法,都定義在Array.prototype身上。

 

var arr = [3,3,4,4];
console.log(arr.__proto__ === Array.prototype); //true
console.log(arr.__proto__.__proto__ === Object.prototype); //true

3.1.4 RegExp()函式

任何正規表示式RegExp()函式的例項。 

var reg = /\d/g;
//等價於
var reg = new RegExp("d","g");
console.log(reg.__proto__ === RegExp.prototype); //true
console.log(reg.__proto__.__proto__ === Object.prototype); //true

3.2基本型別值“包裝類”

Number()String()Boolean()

基本型別值的建構函式,被稱為“包裝類”。JS體系為了完整,所以就人為造出了這三個包裝類,沒有什麼用。


3.2.1 Number()函式

用於建立數字物件:

var a = new Number(3);
console.log(a)
console.log(typeof a)

用內建建構函式建立數字的時候,得到一個物件,這物件的原始值屬性是:

[[PrimitiveValue]]: 3  //這個屬性不可被列舉。

 

它和字面量建立數字的區別:

var a = new Number(3);
var b = 3;
console.log(a == b);   //true
console.log(a === b);  //false

 

Number()建立的物件,可以參與數學運算:

Number的例項是一個物件,但這個物件一旦引數運算,將變為普通Number型別

var a = new Number(3);
a = a * 3
console.log(a)
console.log(typeof a)

 

Number()也可以用來把各種值轉換為陣列(不能轉就是NaN),不需要用new呼叫。

console.log(Number("12"));   //12
console.log(Number("12年")); //NaN
console.log(Number(""));     //0
console.log(Number(false));  //0
console.log(Number(true));   //1
console.log(Number({}));     //NaN
console.log(Number([]));     //0
console.log(Number([1,2]));  //NaN

任何需要轉為數字的隱式轉換,實際上就是在呼叫Number函式。


3.2.2 String()函式

var str = new String("我喜歡你");
console.log(str)

 

String()也可以用來轉換:

console.log(String(123));   //"123"
console.log(String(true));  //"true"
console.log(String([]));    //""
console.log(String([1,2,3])); //"1,2,3"
console.log(String(NaN));  //"NaN"
console.log(String({}));   //"[Object Object]"


3.2.3 Boolean()函式

不管值是false還是true,都能通過if的驗證,都是true

var b = new Boolean(false);
console.log(b)
console.log(typeof b);
if(b){
   alert("真的");
}

3.3內建建構函式之間的關係

就三句話,死記:

1、“{}”物件是被Object new出來的。所以它的__proto__就會指向Object.prototype。
2、任何函式都是Function new出來的例項,所以只要它是函式(建構函式也是函式),它的__proto__就會指向Function.prototype。
3、Function是所有建構函式的媽,它自己也是自己的媽。

小明不是Object new出來的,是People new的,它的__proto__指向People.prototype

 

console.log(Object.__proto__.__proto__ === Object.prototype); //true
console.log(Function.__proto__.__proto__ === Object.prototype); //true
console.log(Object.__proto__ === Function.prototype);  //true
console.log(Function.__proto__ === Function.prototype);  //true
console.log(Array.__proto__ === Function.prototype);  //true
console.log(RegExp.__proto__ === Function.prototype);  //true
console.log(Number.__proto__ === Function.prototype);  //true
console.log(String.__proto__ === Function.prototype);  //true
console.log(Boolean.__proto__ === Function.prototype);  //true

 

 


四、相關的方法、屬性、運算子

4.1 hasOwnProperty()方法

返回布林值(true/false),用來檢測某屬性、某方法是不是在自己身上。

function People(name){
   this.name = name;
}
People.prototype.sayHello = function(){
   alert("你好");
}
var xiaoming = new People("小明");
console.log(xiaoming.hasOwnProperty("name"));     //true
console.log(xiaoming.hasOwnProperty("sayHello")); //false
console.log(xiaoming.hasOwnProperty("toString")); //false

4.2 in運算子

返回布林值(true/false),in運算子可以檢查某個物件有沒有能力呼叫某屬性、某方法,而不管這個屬性或方法是否定義在自己身上,還是原型身上。

字串 in 物件

function People(name){
   this.name = name;
}
People.prototype.sayHello = function(){
   alert("你好");
}
var xiaoming = new People("小明");
console.log("name" in xiaoming);     //true
console.log("sayHello" in xiaoming); //true
console.log("toString" in xiaoming); //true

4.3 constructor屬性

每一個函式的prototype物件都有一個constructor屬性,指向建構函式。

function People(name){
   this.name = name;
}
var xiaoming = new People("小明");
console.log(People.prototype)
console.log(People.prototype.constructor === People); //true
console.log(People.prototype.hasOwnProperty("constructor")); //true
console.log(xiaoming.constructor === People);  //true
console.log(xiaoming.hasOwnProperty("constructor"));  //false


4.4 instanceof運算子

返回布林值,用來檢查某個物件是不是某個函式的例項

o instanceof F

如果F.prototypeo的原型鏈上,返回true,否則返回false

 

function People(name){
   this.name = name;
}
var xiaoming = new People("小明");
function Dog(){
}
console.log(xiaoming instanceof People); //true
console.log(xiaoming instanceof Object); //true
console.log(xiaoming instanceof Dog); //false

 

小題目:

console.log(Object instanceof Object); //true
console.log(Function instanceof Function); //true
console.log(Function instanceof Object); //true
console.log(Number instanceof Function); //true
console.log(Number instanceof Number); //false

 

我們發現object.prototype是所有物件原型鏈的終點,所以我敢說任何原型X一定true

x instanceof Object

Function的prototype出現在自己的__proto__線上,所以是true
console.log(Function instanceof Function); //true

五、練習題

5.1題目1

function A(){}
    function B(){
        return new A(); //返回了引用型別值
    }
    A.prototype = B(); //返回了一個A的例項1
    B.prototype = new B();//返回了一個A的例項2
    var a = new A(); //返回了一個A的例項,賦給a
    var b = new B(); //返回了一個A的例項,賦給b

    console.log(a.__proto__ == b.__proto__);
    console.log(a instanceof A); //true 
    console.log(a instanceof B); // false
    console.log(b instanceof A); //true
    console.log(b instanceof B); //false


5.2題目2

[].constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor.constructor === Function 

答案:true


六、繼承

6.1什麼是繼承

計算機領域,關注兩個類的關係,就是看屬性群之間的關係。

比如:人類和狗狗類屬性群,難免會有交集,但是不完全重合,此時可以認為兩個類沒有任何關係。

 

看看人和學生的屬性群:

 

l 小學生肯定是人,人的屬性學生全有,人能做的事情學生都能做。

l 但是反過來,小學生的屬性和能力,人不一定有。

 

我們說:

“學生”細化了、精分了、更具體了“人”。
“學生”的例項要比“人”的例項少。
“學生”一定是人,但“人”不一定是學生。

術語上,我們稱“學生類”繼承(extend)“人類”。人類叫“父類(超類)”,學生類叫“子類”

 

人又繼承誰呢?人繼承哺乳動物,哺乳動物繼承“生物”...

此時計算機中小學生類繼承extend人類。

A繼承了B,此時要意識到:
    A擁有B的所有屬性和方法
    A的屬性群比B大
    A豐富了B,A把B變得更具體,範圍更小。

6.2 JavaScript實現繼承

JavaScript實現兩個類:People類,Student類,要求People類擁有的屬性和方法,Student類的例項也要擁有People的屬性和方法。Student還能豐富自己類的屬性和方法,很簡單,只要求巧妙設計原型鏈。

//人類
function People(name,age,sex){
   this.name = name;
   this.age = age;
   this.sex = sex;
}
People.prototype.sayHello = function(){
   alert("你好,我是" + this.name);
}
People.prototype.sing = function(){
   alert("都拉米發騷啦稀~~~");
}
// 學生類
function Student(name,age,sex,id,banji,socre){
   People.apply(this, arguments)
   // this.name = name;
   // this.age = age;
   // this.sex = sex;
   this.id = id;
   this.banji = banji;
   this.socre = socre;
}
//下面這條語句可以實現繼承
Student.prototype = new People();
Student.prototype.study = function(){
   alert(this.name + "在學習!");
}
var xiaoming = new Student("小明",12,"男",100001,"初三一班", 100)
xiaoming.study();
xiaoming.sayHello();
xiaoming.sing();

注意:紅色語句一定要出現在Student.prototype.*** = function(){}之前。

 

jQuery創始人John Resig寫了一個小包,20多行程式碼,解決了JS繼承噁心的問題。

https://johnresig.com/blog/simple-javascript-inheritance/

引包之後,這個包改變我們建立JS類的方式(和jQuery一樣改變了寫JS的方式)

//人類
var People = Class.extend({
   init : function(name,age,sex){
       this.name = name;
       this.age = age;
       this.sex = sex;
   },
   sayHello:function(){
       alert("你好,我是" + this.name);
   },
   sing:function(){
       alert("都拉米發騷啦希~~~");
   }
})

//學生類
var Student = People.extend({
   init : function(name,age,sex,id,banji,socre){
       this._super(name,age,sex); //繼承父類的屬性
       // this.name = name;
       // this.age = age;
       // this.sex = sex;
       this.id = id;
       this.banji = banji;
       this.socre = socre;
   },
   study :function(){
       alert(this.name + "在學習!");
   }
})
var xiaoming = new Student("小明",12,"男",100001,"初三一班", 100)
console.log(xiaoming)
xiaoming.study();
xiaoming.sing(); 

七、上升到物件導向

物件導向是一種程式設計思想,兩個字就能概括:自治(自己管理自己),深入理解,就是封裝。每個物件個體僅需要管理自己即可。

物件導向初學階段,當你遇見大量的結構、功能、性質、什麼都一樣的物件的時候,立刻想到用物件導向技術。

現在要給大家一個思維定式,物件導向的時候怎麼程式設計:
  思考初程式中有哪些類,在前期我們的業務僅僅只有一個類,後期類會有多個。
  每個類有哪些方法和屬性,就是他們自己有什麼功能
  每個類之間如何通訊、互動資料、此時就要用到設計模式,比如中介者模式、釋出訂閱模式(觀察者模式)。
  這個類怎麼進行單元測試,如果保證自己這個類魯棒,每個類都魯棒了,整個程式就魯棒。
我們之前的程式設計叫“程式導向”,現在是“物件導向”程式設計(OO)

7.1物件導向-紅綠燈(案例)

考慮一個問題,頁面上要製作一個效果:100個紅綠燈,點選某一個紅綠燈,從紅燈變黃燈,再次點選從黃燈變綠燈,再次點選就綠燈變紅燈...

有一個訊號量,點選按鈕之後,訊號量變化012012012...然後讓divbackground-position進行變化,你要寫100個訊號量、100個盒子、100個事件。

 

頁面上出現的東西就是紅綠燈,而且它們擁有相同的樣子、性質、功能,所以可以讓紅綠燈設計成為一個類。

每一個類負責什麼:①狀態量 ②DOM元素。

每個JS物件中有兩個屬性,一個是狀態屬性,另一個是DOM物件

簡單的說,DOM物件現在成為JS物件的一個屬性。

這個類有哪些屬性?
  DOM屬性
  顏色屬性

哪些方法?
  初始化方法 init()
  換顏色方法 changeToColor()
  繫結事件方法 bindEvent()

 

第一步:DOM結構和CSS樣式,確保放一個div元素能看見燈出來了。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style type="text/css">
     *{ margin: 0; padding: 0;}
     div.honglvdeng{
         position: relative;
         width: 140px;
         height: 328px;
         background: url(./images/honglvdeng.jpg);
     }
    </style>
</head>
<body>
    <div id="box">
        <div class="honglvdeng"></div>
    </div>
</body>
</html>
//第二步:建立紅綠燈類
//new Honglvdeng()時,會執行建構函式中的語句
//所以在建構函式中建立一個DOM物件,然後讓它上樹
function Honglvdeng(){
   //每一個類中有兩個屬性:狀態量、DOM
   this.dom = null;
   //狀態屬性
   this.state = 0;
   //初始化方法
   this.init();
   //事件監聽方法
   this.bindEvent();
}
//為了程式美觀,整潔,好更改和可插拔性高,方便維護,將DOM和上樹語句都寫在初始化方法中
Honglvdeng.prototype.init = function(){
   //建立DOM
   this.dom = document.createElement('div');
   //給DOM新增類名
   this.dom.className = 'honglvdeng';
   //上樹
   document.getElementById("box").appendChild(this.dom);
}

//第三步:新增事件監聽
Honglvdeng.prototype.bindEvent = function(){
   //備份this,因為事件監聽裡面的this表示dom元素本身
   var self = this;
   this.dom.onmouseenter = function(){
       // 改變訊號量
       // self.state++;
       // if(self.state > 2) self.state = 0
       //判斷簡寫
       self.state = ++self.state % 3;
       self.dom.style.backgroundPositionX = -155 * self.state +'px';
   }
}
//第四步:例項化100個紅綠燈
var count = 100;
while(count--){
   new Honglvdeng();
}

 

相關文章