建立函數,3種:
1 聲明:只有聲明方式建立的函數才能被聲明提早
function fun(arg1, arg2, ...){ statement; return value;}
2 函數直接量:沒法被聲明提早
var fun = function(arg1, arg2, ...){ statement; return value;};
揭示:函數實際上是一個對象,函數名僅僅是一個引用函數對象的普通變量。
3 用new:
var fun = new Function("arg1", "arg2", ..., "statement; return value;");javascript
重載(overload):相同函數名,不一樣參數列表的多個函數,在調用時,根據傳入參數的不一樣,自動選擇匹配的函數執行
目的目的:減小API的數量,減輕調用者的負擔java
when:若是多個函數只是流程不一樣,其實都是一類事,就能夠將多個函數命名爲同名函數,不一樣參數
how:js語法不支持重載!js中後定義的同名函數會覆蓋先定義的。
arguments:函數中自動接收全部傳入函數的參數值的類數組對象。
類數組對象:長得像數組的對象
類數組對象和數組的相同點:都用[i]方式訪問每一個元素;length屬性記錄元素的個數;for循環遍歷每一個元素。數組
對象的建立,3種:
1 對象直接量:
var obj = {
屬性名: 屬性值;
...,
方法名: function(){},
...
};
注意:每一個屬性名和方法名不用加引號,可是底層都是用字符串存儲。瀏覽器
this:引用當前正在調用函數的對象!app
和定義在哪一個對象中無關!只和調用時 . 前的對象有關!ide
固定場景:a.在對象本身的方法中,訪問本身的屬性函數
b.在構造函數中,指代正在建立的新對象性能
c.在原型對象的共有方法中,指代未來調用共有方法的子對象this
注意:不是任何對象直接調用的函數中this默認指window對象。
when:對象本身的方法中,想訪問對象本身的屬性必須用 this.屬性名
2 使用new:
var obj = new Object();//建立一個Object類型的空對象
obj.屬性名 = 屬性值;//等效於 obj["屬性名"] = 屬性值;
...
obj.方法名 = function(){... this.屬性名 ...};
when:若是在建立對象時,暫時不知道包含哪些屬性,就先建立一個對象,再添加屬性spa
注意:js中的對象,能夠隨時添加新屬性,只要給不存在的屬性賦值,就自動添加
本質:js中一切對象都是hash數組:
a.均可以隨時添加新的屬性和方法
b.訪問對象中一個不存在的屬性,返回undefined
c.屬性名和方法名其實至關於hash數組中的key
d.均可用for in遍歷每一個屬性
3 反覆建立多個相同類型和結構的對象;
a.定義構造函數,描述一類對象的統一結構
構造函數:描述一類對象的統一結構德函數
when:只要反覆建立相同結構的多個對象前,都須要先定義構造函數,再建立對象。
how:
function 類型名/構造函數名(屬性參數列表){
this.屬性名 = 屬性參數值;
...
this.方法名 = function(){
... this.屬性名...
}
}
b.使用new調用構造函數建立對象並添加屬性
var obj = new 構造函數名(屬性值列表);
構造函數中的this,指new正在建立的對象
面向對象:
原型和原型鏈:
原型:保存全部子對象的公共成員的父級對象
每一個構造函數,都有一個prototype屬性,引用本身的原型對象
每一個子對象,都有一個 __proto__ 屬性,繼承自構造函數的原型對象
繼承:父對象的成員,自對象無需建立,可直接使用!
對象建立:
a.建立空對象
b.設置新對象的 __proto__ 屬性繼承構造函數的原型對象
c.調用構造函數,添加成員
d.返回地址
when:同一類型的子對象共享的屬性和方法,都要放在構造函數的原型對象中。
優勢:代碼重用,節約內存!
刪除屬性:
a.自有屬性: delete 對象.屬性名;
b.共有屬性:只能經過構造函數的原型對象刪除 delete 構造函數.prototype.屬性名
判斷自有屬性與共有屬性:
a.判斷自有屬性:判斷指定屬性是否直接保存在當前對象本地
var isSelf = obj.hasOwnProperty("屬性名");
true:是自有屬性
false:沒法肯定是否爲共有
b.判斷共有屬性:同時知足兩條件:
不是自有: obj.hasOwnProperty("屬性名") == false;
可是在原型中有: obj.屬性名 !== undefined
<script type="text/javascript"> //String類型支持trim(),去掉先後的空格 //若是當前瀏覽器不支持trim if(String.prototype.trim === undefined){ console.log("當前瀏覽器不支持trim(),自定義trim()"); String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, ""); }; } var str = " hello js, I like "; console.log(":" + str + ":"); str = str.trim(); console.log(":" + str + ":"); </script>
總結:不使用 obj. 方式訪問對象,都去做用域鏈找,只要使用 obj. 訪問成員,都去原型鏈找。
原型相關API:
1. 獲取原型對象,2種:
a. 構造函數.prototype
b. 子對象.__proto__ :內部屬性,可能被禁用
儘可能使用Object.getPrototypeOf(子對象);
2. 判斷對象間是否有繼承關係:
var hasInherit = 父對象.isPrototypeOf(子對象);
3. instanceof:判斷一個對象是不是指定構造函數的實例
function Student(){};
var jack = new Student();
jack instanceof Student -> true
多態:同一事物,在不一樣狀況下表現出不一樣樣子
重寫:override:若是子對象以爲父對象的成員很差用,可在本地定義同名成員,覆蓋父對象的。
在原型對象中
如何添加共有屬性:只能經過原型對象
構造函數.prototype.屬性名 = 值;
原型鏈:由各級對象的 __proto__ 逐級繼承造成的鏈式關係
規則:在訪問對象屬性時,只要本身有,就不去父級找,若是本身沒有,纔去父級找,若是到Object.prototype都沒找到,就返回undefined。
和做用域鏈的比較:
做用域鏈:控制變量的使用順序:全部不帶.的變量,默認都去做用域找
原型鏈:控制對象的屬性的使用順序:全部用.訪問的對象屬性,都去原型鏈找
每一個對象內部有一個屬性: class 記錄了建立對象時使用的類型名
訪問對象內部的class: 只能調用原生的toString()
Object.prototype.toString();//"[object Object]"
對象 class
強行調用原生toString():
原生toString.call(替代this的對象)
call作兩件事:a.執行函數;b.替換this
Object.prototype.toString(); this->Object.prototype
Object.prototype.toString.call(obj); this->obj->在執行時,至關於obj.toString()
<script type="text/javascript"> //判斷一個對象是不是數組 var obj1 = {};//Object var obj2 = [];//Array var obj3 = function(){};//Function var obj4 = {}; obj4.__proto__ = []; //typeof沒法區分數組和對象 console.log(typeof obj1);//object console.log(typeof obj2);//object console.log(typeof obj3);//function console.log(typeof obj4);//object console.log("=================="); //1. isPrototypeOf 不但檢查直接父對象,並且檢查整個原型鏈 console.log(Array.prototype.isPrototypeOf(obj1));//false console.log(Array.prototype.isPrototypeOf(obj2));//true console.log(Array.prototype.isPrototypeOf(obj3));//false console.log(Array.prototype.isPrototypeOf(obj4));//true console.log("=================="); //2. constructor 也可檢查整個原型鏈 console.log(obj1.constructor == Array);//false console.log(obj2.constructor == Array);//true console.log(obj3.constructor == Array);//false console.log(obj4.constructor == Array);//true console.log("=================="); //3. instanceof 也可檢查整個原型鏈 console.log(obj1 instanceof Array);//false console.log(obj2 instanceof Array);//true console.log(obj3 instanceof Array);//false console.log(obj4 instanceof Array);//true console.log("=================="); //4. 每一個對象內部有一個屬性: class 記錄了建立對象時使用的類型名 console.log(Object.prototype.toString.call(obj1) == "[object Array]");//false console.log(Object.prototype.toString.call(obj2) == "[object Array]");//true console.log(Object.prototype.toString.call(obj3) == "[object Array]");//false console.log(Object.prototype.toString.call(obj4) == "[object Array]");//false console.log("=================="); //5. ES5: isArray if(Array.isArray === undefined){ console.log("Array.prototype.isArray not exsit."); Array.isArray = function(obj){ return (Object.prototype.toString.call(obj) == "[object Array]"); }; } console.log(Array.isArray(obj1));//false console.log(Array.isArray(obj2));//true console.log(Array.isArray(obj3));//false console.log(Array.isArray(obj4));//false </script>
自定義繼承:
a.僅修改一個對象的父對象:
子對象.__proto__ = 父對象;
Object.setPrototypeOf(子對象, 父對象);
b.直接修改構造函數的原型對象,可同時修改以後建立的全部子對象的父對象:
時機:必須在建立第一個對象以前完成,才能保證對象間的一致性。
c.兩種類型間的繼承:即擴展結構(extends),又繼承原型(inherit)
抽象:將多個子類型的相同屬性和方法,集中提取到一個公共的父類型中定義。
how:3步:
c1. 將多個子類型的相同屬性和方法,抽象到一個公共的父類型中集中定義
c2. 借用構造函數:在子類型構造函數中調用父類型構造函數
問題:若是直接調用父類型構造函數:this->window
解決:強行調用,更換this:父類型構造函數.call(this, 參數1, 參數2, ...);
問題:call要求每一個參數都要重複寫一遍
解決:使用apply傳入arguments,apply可打散arguments,再單獨傳入:父類型構造函數.apply(this, arguments);
總結:只有兩個API可打散數組參數:
arr1.concat(arr2); -> arr1.concat(arr2[0], arr2[1], ...);
fun.apply(obj, arr); -> obj.fun(arr[0], arr[1], ...);
c3. 讓子類型的原型繼承父類型的原型:Object.setPrototypeOf(子類型的原型, 父類型的原型);
call和apply:
相同:都是強行調用一個函數,並替換this
差異:傳入參數的方式:
call:要求每一個參數必須單獨傳入
apply:要求全部參數以一個數組的方式總體傳入
<script type="text/javascript"> function Flyer(fname, speed){ this.fname = fname; this.speed = speed; } Flyer.prototype.fly = function(){ console.log(this.fname + " is flying, on speed " + this.speed); }; function Enemy(fname, speed, score){ Flyer.call(this, fname, speed); this.score = score; } Enemy.prototype.getScore = function(){ console.log(this.fname + " score is " + this.score); }; Object.setPrototypeOf(Enemy.prototype, Flyer.prototype); function Bee(fname, speed, award){ Flyer.apply(this, arguments); this.award = award; } Bee.prototype.getAward = function(){ console.log(this.fname + " award is " + this.award); }; Object.setPrototypeOf(Bee.prototype, Flyer.prototype); var e = new Enemy("enemy001", 50, 20); e.fly(); e.getScore(); var b = new Bee("bee001", 5, 2); b.fly(); b.getAward(); </script>
*****ES5
對象的屬性:
對象:屬性的集合
2大類:
a. 命名屬性:自定義的可用,直接訪問的屬性
a1. 數據屬性:直接保存一個數據的屬性
a2. 訪問器屬性:專門保護另一個數據屬性的特殊屬性,不直接保存數據
b.內部屬性:對象內部自動包含的,沒法用.訪問到的屬性,好比:class
***數據屬性:
ES5中規定,每一個數據屬性都包含4大特性:
value:時機存儲屬性值的特性
writable:控制當前屬性是否可修改 bool
enumerable:控制當前屬性可否被 for in 遍歷到 bool
configurable:控制當前屬性可否被刪除,或可否修改其餘特性
如何訪問屬性的特性:
查看屬性的特性: var attrs = Object.getOwnPropertyDescriptor(obj, "屬性名");
設置屬性的特性: Object.defineProperty(obj, "屬性名", {value: 值, writable: true/false, enumerable: true/false, configurable: true/false});
注意:通常修改特性時,都要將configurable設置爲false:不許修改其餘特性;不可逆。
添加一個新屬性,並設置四大屬性:
Object.defineProperty(obj, "屬性名", {value: 值, writable: true/false, enumerable: true/false, configurable: true/false});
注意:使用defineProperty添加的新屬性,四大特性默認值爲false!
對象直接量中的屬性:四大特性默認值爲true
同時定義或添加多個屬性,並設置四大特性:
Object.defineProperty(obj, {
"屬性名": {value: 值, writable: true/false, enumerable: true/false, configurable: true/false},
...
});
<script type="text/javascript"> var emp = {id:1001, name: "jack", salary: 10000}; console.log(Object.getOwnPropertyDescriptor(emp, "id"));//Object {value: 1001, writable: true, enumerable: true, configurable: true} Object.defineProperty(emp, "id", {writable: false});//修改emp的id屬性爲只讀 emp.id = 1002; Object.defineProperty(emp, "salary", {enumerable: false});//修改emp的salary屬性爲不可遍歷 for(var key in emp){ console.log(key + ":" + emp[key]);//id:1001 name:jack } </script>
靜態方法:不須要實例化對象,就直接調用的方法。
方法定義在原型對象中,仍是定義在構造函數上:
若是隻但願當前類型的子對象才能用,就放在原型對象中;
若是不但願實例化任何對象,就能夠直接調用,就放在構造函數上。