前端開發【第5篇:JavaScript進階】

語句

複合表達式和空語句javascript

複合表達式意思是把多條表達式鏈接在一塊兒造成一個表達式java

{
let a = 100; let b = 200; let c = a + b; }

注意這裏不能再塊級後面加分號,塊內都有縮進不是必須的,塊內的原始語句經過;號進行分割python

空語句在塊級或者()後面加分號表示爲空語句,能夠理解爲何都沒有的語句,在語句後面加分好";"shell

就算要寫空語句最好也在後面加上註釋編程

for(let i = 0, i < a.lenght; a[i++] = 0) /* empty */ ;

聲明語句json

變量聲明:let和const不須要說了數組

函數聲明瀏覽器

函數聲明的語法以下:app

function funcname([arg1, [arg2, ....]]) {
    statements
}

函數的定義也能夠寫成以下:函數式編程

let f function(x) {return x +1}

條件語句

和Python的條件判斷基本類似

if(表達式爲真){
    爲真的時候執行的語句  
}

何時爲假:nul、undefind、false、0、NaN

else和 else if  

let n = 100;
if(n === 1){
    //執行代碼塊1
}else if (n === 2){
    //執行代碼塊2
}else if (n === 3){
    //執行代碼塊3
}else{
    //若是上面的都沒有執行執行這裏
}

switch語句感受和shell中的case語句很像,很是適合這種多匹配的表達式

switch (n){
    case 1:
        // 若是n === 1 從這裏執行
        break;  // 執行完後跳出switch語句
    case 2:
        // 若是n === 2 從這裏執行
        break;  // 執行完後跳出switch語句
    case 3:
        // 若是n === 3 從這裏執行
        break;  // 執行完後跳出switch語句
    default:
        // 若是上面的都沒有匹配執行這裏
        break
}

下面的例子更貼近實戰

// switch實戰
function convert(x) {
    switch (typeof x){
        case 'number':
            // 若是是數將它轉換爲16進制
            return x.toString(16);
        case 'string':
            // 若是是字符串
            return '"' + x + '"';
        default:
            // 使用普通方法轉換其餘類型
            return String(x)
    }
}

循環

while循環和python中的同樣

n = 0;
while (n < 10){  // 當條件不成立的時候中止循環不然會一直執行
    //要執行的語句
    console.log(n);
    n++
}

JavaScript中還有一個很是有意思的do while至少執行一次的while循環它不像while這麼經常使用,由於這種狀況少~~!

let n1 = 0;
do {
    // 這裏是while的執行內容 
    console.log('執行一次');
    console.log(n1++)
}while (n1++ < 10);  // 這裏是條件結果,當條件爲假的時候中止

for循環有些特殊,有兩種for循環一個一個看先看第一種與while循環相似的

for(let i = 0; i < 10; i){  // (初始值; 中止條件; 初始值操做 能夠理解用來達成中止條件的操做)
    console.log(i);            //  循環內容操做
    console.log('Hello World')
}

它很適合用在循環數組對象的地方

let loopArray = ['a', 'b', 'c', 'd', 'e'];
for(let i = 0; i < loopArray.length; i++){
    // 這個循環中咱們能夠看出來這個i 初始值爲0,能夠理解爲數組的下標0開始循環當小於數組長度的時候中止
    // 循環一次自加1達到了循環的效果
    console.log(loopArray[i])
}

當循環普通對象的時候又有一個新的循環for  / in  相似python中的循環,很嗆人~~

在JavaScript中,對象的屬性分爲可枚舉和不可枚舉之分,它們是由屬性的enumerable值決定的。可枚舉性決定了這個屬性可否被for…in查找遍歷到。若是一個對象中的屬性是可枚舉的那麼它們枚舉的順序根據各自的瀏覽器可能不一樣知道就能夠了

正常的循環對象或者數組對象和Python差很少:

function Person(name) {
    this.name = name
}

Person.prototype = {
    age : 18,
    job : 'shuaige'
};

let shuaige = new Person('shuai');

for(let i in shuaige){
    console.log(i)
}

break、continue、return 沒必要說了

異常處理

和Python中的異常處理差很少頗有意思

// 異常處理
try{
    // 這裏若是沒有異常執行到尾的操做
}catch(e){
    // 這裏是若是出現異常以後執行的操做   
}finally {
    // 這裏是無論你是否只是成功仍是失敗都執行的操做
}

功能和Python異常同樣,都是能夠表面異常後致使程序終止的地方均可能須要加上異常處理,看本身程序的設計了

對象

終於到對象了,弱弱的問下你有對象嗎?

言歸正傳,MD JavaScript中的普通對象能夠理解爲Python中的字典,OK只是能夠理解爲Python中的字典,可是有區別的(感受是Python中字典和類的混合體~~忽略這一句話)

建立對象很簡單

一、能夠直接經過Object.create()建立對象

二、或者經過直接建立let a = {} 也建立了一個對象

三、或者經過new建立對象 let a = new Object()  /   new Date  / new Array() / new RegExp()

 

原型

每一個JavaScript對象除了null都和另外一個對象相關聯,「另外一個」對象就是咱們熟知的「原型」,

全部經過對象直接建立的對象都具備一個原型對象,並能夠經過JavaScript代碼Object.prototype得到原型對象的引用,舉個例子:

經過new Object()建立的對象也繼承了Object.prototype,一樣new Date() 建立的對象也繼承了Date.prototype

原型的誕生:

Brendan Eich思考以後,決定借鑑C++和java的new命令,將new命令引入了JavaScript,在傳統的面向對象的語言裏,new 用來構造實例對象,new 會調用構造函數,可是傳統面向對象的語言new 後面的是類,內部機制是調用構造函數(constructor),而Brendan Eich簡化了這個操做,在JavaScript裏面,new 後面直接是構造函數,如是咱們能夠這麼寫一個Person類:

        function Person(name) {
            this.name = name
        }
        let personOne = new Person("Brendan Eich");
        console.log(personOne.name)

這樣就建立了一個新的實例了。可是new有缺陷。用構造函數生成實例對象是沒法沒法共享屬性和方法,例以下面代碼:

    function Person(name) {
            this.name = name;
            this.nation = 'USA';
        }
        let person1 = new Person("Brendan Eich");
        let preson2 = new Person("Shuai Ge");

        person1.nation = "China";

        console.log(person1.nation);  // China
        console.log(preson2.nation);  // USA

每個實例對象,都有本身的屬性和方法的副本。這不只沒法作到數據共享,也是極大的資源浪費。和JavaScript工廠模式的缺點同樣,過多重複的對象會使得瀏覽器速度緩慢,形成資源的極大的浪費。

考慮到這一點,Brendan Eich決定爲構造函數設置一個prototype屬性,這個屬性都是指向一個prototype對象。下面一句話很重要:全部實例對象須要共享的屬性和方法,都放在這個Prototype對象(原型對象)裏面;那些不須要共享的屬性和方法,就放在構造函數裏面

實例對象一旦建立,將自動引用prototype對象的屬性和方法。也就是說,實例對象的屬性和方法,分紅兩種,一種是本地的,另外一種是引用的。如是咱們能夠改寫下上面的程序:

       function Person(name) {
            this.name = name;
        }
        // 咱們不須要指定prototype對象當咱們建立對象的時候默認會生成
        Person.prototype = {nation: "USA"};

        let person1 = new Person("Brendan Eich");
        let person2 = new Person("Shuai Ge");
        
        console.log(person1.nation);
        console.log(person2.nation);

當咱們這樣寫程序時候Person.prototype.nation = 'China'; 全部實例化的類的nation都會變成China。

因爲全部的實例對象共享同一個prototype對象,那麼從外界看起來,prototype對象就好像是實例對象的原型,而實例對象則好像"繼承"了prototype對象同樣。prototype只是提供了實現JavaScript繼承的一個很方便的途徑和手段。

原型鏈

知道原型的做用和原型的由來後,咱們在看下原型鏈

function Person(name) {
    this.name = name
}
Person.prototype = {
    'key1': 'value1',
    'key2': 'value2'
};

let person1 = new Person('shuaige');

console.log(person1.__proto__ == Person.prototype);  // true
console.log(person1.constructor);  // [Function: Object]

 

如上圖所示,咱們經過prototype指向了一個共享的屬性,在實例化的對象中person1和person2中他們都有一個__proto__屬性指向了,prototype

在prototype中又有一個constructor的屬性它指向了構造函數

因爲全部的實例對象共享同一個prototype對象,那麼從外界看起來,prototype對象就好像是實例對象的原型,而實例對象則好像"繼承"了prototype對象同樣。prototype只是提供了實現JavaScript繼承的一個很方便的途徑和手段。

能夠說,由prototype、constructor、__proto__三個指針構成了整個javascript的原型鏈。

能夠看出,原型的屬性是共享的,所以,constructor屬性也是共享的,能夠經過實例訪問

 可是,另外一方面,沒法經過實例修改原型中的值。若是在實例中添加了一個屬性,而且屬性名跟原型中的相同,那麼將會在實例中建立該屬性並屏蔽原型中的那個屬性。能夠經過delete 刪除本身的屬性

function Person(name) {
    this.name = name

}
Person.prototype = {
    constructor: Person,
    'key1': 'value1',
    'key2': 'value2'
};

let person1 = new Person('shuaige');
console.log(person1.key1);
person1.key1 = 'shuaige';
console.log(person1.key1);
delete person1.key1;
console.log(person1.key1);


結果:
/* 
value1
shuaige
value1

*/

in 操做符及 hasOwnProperty()方法,看下面的檢查屬性

javascript中檢測對象屬性有兩種方式,一種是使用in操做符,一種是hasOwnProperty()方法。這兩種方式的區別是:in操做符會同時檢測原型和實例,而hasOwnProperty方法只會檢測實例。看下面的例子:

function Person(name) {
    this.name = name

}
Person.prototype = {
    constructor: Person,
    'key1': 'value1',
    'key2': 'value2'
};

let person1 = new Person('shuaige');
// 判斷屬性中是否有name,取值範圍是實屬性和原型屬性
console.log("name" in person1);
console.log("key1" in person1);
// 判斷name是不是實例屬性,不會判斷原型屬性
console.log(person1.hasOwnProperty("name"));

原型的重寫

因爲爲原型一個個單獨添加屬性和方法不利於封裝,所以更好的作法是用封裝好的屬性和方法的字面量來重寫整個原型對象。以下:

function Person(name) {
    this.name = name

}
Person.prototype = {
    constructor: Person,
    'key1': 'value1',
    'key2': 'value2'
};

注意constructor 的屬性,若是不在新的原型對象中從新設定,那麼constructor屬性將不在指向Person。由於這裏徹底重寫了默認的prototype對象,所以constructor屬性將指向新的contructor—object構造函數。

可是,若是使用重寫原型的方法,就至關於切斷了構造函數與最初原型的聯繫。一個實例只擁有一個惟一的原型,而實例中的指針又只指向原型,而不是構造函數。所以,原型的重寫最好在實例以前,不然實例將指向最初的原型以下例子:

function Person() {
}
let person1 = new Person();

Person.prototype = {
    constructor: Person,
    'name': 'dashuaige',
    'key1': 'value1',
    'key2': 'value2'
};
console.log(person1.name);  // undefined
console.log(person1.constructor.prototype.name);  // dashuaige

屬性的查詢

function Person() {
}


Person.prototype = {
    constructor: Person,
    'name': 'dashuaige',
    'key1': 'value1',
    'key2': 'value2'
};
let person1 = new Person();

console.log(person1.name);  // 獲取name屬性
console.log(person1["name"]);  // 獲取name屬性

經過.訪問的時候相似C和java對象的靜態字段訪問相似

第二種方法和python中的字典相似

刪除屬性

經過delete進行刪除

function Person() {
}


Person.prototype = {
    constructor: Person,
    'name': 'dashuaige',
    'key1': 'value1',
    'key2': 'value2'
};
let person1 = new Person();
person1.age = 18;  //設置一個屬性age = 18
person1['hobby'] = 'belle';  //設置一個屬性hobby = belle

delete person1.age;
delete person1.hobby;

檢查屬性

JavaScript對象能夠看作屬性的集合,能夠經過in、hasOwnProperty、propertyIsEnumerable來完成查詢

in

in左側是屬性名(字符串)右側是對象,若是對象自有屬性或繼承屬性中包含這個屬性返回true

hasOwnProperty

hasOwnProperty檢測給定的名字(字符串)是不是對象的自有屬性,若是是對象的自有屬性返回true若是是原型屬性返回false

function Person() {
}


Person.prototype = {
    constructor: Person,
    'name': 'dashuaige',
    'key1': 'value1',
    'key2': 'value2'
};
let person1 = new Person();
person1.age = 18;  //設置一個屬性age = 18
person1['hobby'] = 'belle';  //設置一個屬性hobby = belle

console.log("hobby" in person1);  // true  判斷給定的左側名字是否在自有屬性或原型屬性中
console.log(person1.hasOwnProperty("hobby"));  //若是是自有屬性返回 true
console.log(person1.hasOwnProperty("name"));   //若是是原型屬性返回 false

枚舉屬性

let o = {'x': 1, 'y': 2, 'z': 3};
o.propertyIsEnumerable("toString");  // 設置爲不可枚舉

for(p in o)
    console.log(p)

爲何要有這個枚舉屬性

有許多工具庫給Object.prototype添加了新的方法或屬性,這些方法和屬性能夠被全部的對象繼承並使用,然而在ECMAScript5標準前,這些方法是不能定義爲不可枚舉的。

所以他們均可以被for/in 循環中枚舉出來。爲了不這種狀況須要過濾for/in循環中的屬性,下面兩種方式是最多見的

for(let i in person1){
    if (!person1.hasOwnProperty(i)) continue;  // 跳過繼承屬性
    console.log(i)
}

for(let i in person1){
    if(typeof person1[i] == "function") continue;  // 跳過方法
}

getter和setter

首先明確一下他是一個對象存儲屬性,set 、 get  ,一個只有只讀方法一個只有只寫方法

let o = {
    // 普通的數據屬性
    "name": "None",
    
    // 存取器屬性都是成對定義的函數
    get accessor_prop(){/* 這裏是函數體*/},
    set accessor_prop(value){/* 這裏是函數體 */}
};

實例

let sex = {
    "sex": "man" ,
    get accessor_sex() { return this.sex },
    set accessor_sex(value){ return this.sex = value},
};

// 查詢
console.log(sex.accessor_sex);
// 設置
console.log(sex.accessor_sex = "woman");

或許你會以爲這是畫蛇添足的,由於咱們徹底能夠忽視get和set,直接讓sex方法具有兩種權限。 但之因此咱們將get和set單獨拿出來,是爲了更加清晰地理解ECMAScript5對javascript對象鍵值操做中,一個更爲嚴謹的詮釋。

類屬性

 想得到對象的類能夠經過toString()方法,經過直接量或者new建立或者自定義的函數建立的對象的類屬性都是Object,所以對自定義類來講,沒有辦法經過類屬性區分對象類

可擴展屬性

對象的可擴展屬性表示對象是否能夠添加新屬性,全部內置對象屬性都是顯示可擴展的,能夠經過

function Person() {
}

Person.prototype = {
    constructor: Person,
    'name': 'dashuaige',
    'key1': 'value1',
    'key2': 'value2'
};
let person1 = new Person();

console.log(Object.isExtensible(person1));  // true 判斷是否爲可擴展對象
console.log(Object.preventExtensions(person1));
console.log(Object.isExtensible(person1));  // false
person1.test = "valueTest";
console.log(person1);  // 屬性沒法添加成功

序列化和反序列化

JavaScript提供了一個全局的對象JSON

let testObject = {"key1": "value1", "array1": [1, 2, 3, 4, 5]};  // 定義一個對象
let testObjectDumps = JSON.stringify(testObject);  // 序列化爲json格式
console.log(typeof testObjectDumps, "|", testObjectDumps);  // string | {"key1":"value1","array1":[1,2,3,4,5]}
let testObjectLoad = JSON.parse(testObjectDumps);
console.log(typeof testObjectLoad, "|", testObjectLoad);  // object | { key1: 'value1', array1: [ 1, 2, 3, 4, 5 ] }

數組

和Python差很少可是有些特殊的地方好比

稀疏數組

// 稀疏數組
let a = new Array(5);  // 建立一個數組對象,長度爲5,可是沒有元素,我擦擦擦~
let b = [];  // 建立一個空數組,長度爲0
a[1000] = 0;  // 賦值添加元素,數組長度爲10001


let a1 = [,];  // 次數組沒有元素長度爲1
let a2 = [undefined];  // 次數組包含一個值爲undefined

console.log(0 in a1);  // false: a1在下標爲0的索引處沒有元素
console.log(0 in a2);  // true : a2在下標爲0的索引處元素

數組長度能夠修改元素我擦

console.log([].length);  // 輸出長度爲0
console.log([1, 2, 3].length);  //輸出長度爲3
let a = [1, 2, 3, 4, 5];
console.log(a.length);  // 輸出長度爲3

// 見證奇蹟的時候到了
a.length =  3; console.log(a);  // [ 1, 2, 3 ] 居然是截取保留前3個值
a.length = 0 ; console.log(a);  // [] 刪除全部的元素
a.length = 5 ; console.log(a);  // [ <5 empty items> ] 長度爲5可是沒有元素,就像new Array()

數組添加元素刪除元素

let a = [1, 2, 3, 4];  // 定義一個新數組
a[0] = 0; console.log(a);  //  若是元素存在會被替換[ 0, 2, 3, 4 ] 可是若是是空的這也是一個增長元素的方法
a.push(5); console.log(a);  // 在數組對象後面追加元素[ 0, 2, 3, 4, 5 ] 亦能夠追加多個元素

刪除就簡單了

能夠經過length。。。 也能夠經過delete刪除指定元素

數組方法

join 轉化爲字符串並拼接

let a = [1, 2, 3, 4];
console.log(a.join("-"));  // 結果1-2-3-4  

reverse()翻轉數組方法

let a = [1 ,2, 3, 4, 5]; console.log(a.reverse());  //  [ 5, 4, 3, 2, 1 ]  

sort()排序方法

默認根據字母表排序若是有undefined將放到最後面

concat() 擴展數組

這個連接的是元素而不是數組自己,這個擴展數不會遞歸扁平化數組,也不會修改數組

let a = [1, 2, 3];
console.log(a.concat(4 ,5));   // [ 1, 2, 3, 4, 5 ]
console.log(a.concat([4, 5]));  // [ 1, 2, 3, 4, 5 ]
console.log(a.concat([4, 5], [6, 7]));  //  [ 1, 2, 3, 4, 5, 6, 7 ]
console.log(a.concat(4, [5, [6, 7]]));  // [ 1, 2, 3, 4, 5, [ 6, 7 ] ]

slice()數組切片返回指定數組

// slice()切片方法
let a = [1, 2, 3, 4, 5];
console.log(a.slice(0,3));   // 返回[ 1, 2, 3 ]
console.log(a.slice(3));  // 返回 [ 4, 5 ]
console.log(a.slice(1, -1));  // 返回 [ 2, 3, 4 ]
console.log(a.slice(-3, -2));  // 返回 [ 3 ]

splice()方法也是刪除插入元素的方法可是和slice和concat有本質區別是它會修改調用的數組

// splice
// splice 第一個參數指定了插入和刪除的起始位置,第二個參數指定了應該從數組中刪除多少個元素
let a = [1, 2, 3, 4, 5, 6, 7, 8,];
// 若是第二個元素起始點到後面的元素都被刪除
console.log(a.splice(4), a);  // 返回[ 5, 6, 7, 8 ]  a被修改成[ 1, 2, 3, 4 ]

// 如今a是 [1, 2, 3, 4]
console.log(a.splice(1, 2), a);  // 從1開始刪兩個是[ 2, 3 ] a被修改成[1, 4]

// splice 前兩個指定了起始位置和刪除元素的個數,後面跟着的是插入的元素插入的位置是前面定義的位置
let b = [1, 2, 3, 4,];
console.log(b.splice(2, 0, 'a', 'b'), b);  // []  從第二個元素開始插入[ 1, 2, 'a', 'b', 3, 4 ]

push和pop方法

push和pop方法容許將數組當作棧來使用,push方法在數組尾部添加一個或多個元素,並返回新的長度,pop相反;它刪除數組最後一個元素,減小數組長度並返回它刪除的值,組合push和pop可以實現JavaScript數組實現先進先出的棧

// push()   pop()

let stack = [];
console.log(stack.push(1, 2), stack);  // 長度爲2 [ 1, 2 ]
console.log(stack.push(3), stack);  // 長度爲3 [ 1, 2, 3 ]
console.log(stack.push([11, 22, 33,]), stack);  // 長度爲4 [ 1, 2, 3, [ 11, 22, 33 ] ]
console.log(stack.pop(), stack);  // 取出最後一個元素[ 11, 22, 33 ]   當前stack 爲: [ 1, 2, 3 ]

unshift和shift

unshift和shift 與push和pop相似只是push和pop在尾部操做unshift和shift在頭部操做

let stack = [];
console.log(stack.unshift(1, 2), stack);  // 長度爲2 [ 1, 2 ]
console.log(stack.unshift(3), stack);  // 長度爲3 [ 3, 1, 2 ]
console.log(stack.unshift([11, 22, 33,]), stack);  // 長度爲4 [ [ 11, 22, 33 ], 3, 1, 2 ]
console.log(stack.shift(), stack);  // 取出最前一個元素[ 11, 22, 33 ] 當前stack爲: [ 3, 1, 2 ]

ECMAScript5中數組方法

forEach方法從頭至尾遍歷數組,爲每一個元素調用指定函數

let stack = [1, 2, 3, 4, 5, 6];
let sum = 0;
stack.forEach(function (value) {sum += value});  // sum 累加數組元素裏面的值
console.log(sum);  // 輸出sum的值

// forEach()使用三個參數: 數組元素/元素的索引/數組自己
stack.forEach(function (v, i, a) {a[i] = v + 1});
console.log(stack);

注意forEach沒法在全部元素都傳遞給調用的函數以前終止遍歷,簡而言之就是活沒幹完不準停,若是中途想中止須要放在一個異常處理裏

map()方法將調用的數組每一個元素傳遞給指定的函數,並返回一個數組,它包含該函數的返回值

// map()
let testArrayA = [1, 2, 3, 4, 5];
let testArrayB = testArrayA.map(function (value) {
    return value * value
});
console.log(testArrayB);  // 輸出結果是:[ 1, 4, 9, 16, 25 ]

filter()篩選方法

// filter
let testArray = [1, 2, 3, 4, 5];
console.log(testArray.filter(function (value){return value < 3}));  // [ 1, 2 ]
console.log(testArray.filter(function (value, i){return i%2===0}));  // [ 1, 3, 5 ]

// 也可使用它來排除空元素
let testArrayA = [1, 2, 3, 4, null, undefined, 5, 6];
console.log(testArrayA.filter(function (value) {
    return value !== null && value !== undefined
}));  // [ 1, 2, 3, 4, 5, 6 ]

every()和some()判斷數組元素

every()和some()方法是數組的邏輯斷定,它們對數組元素應用指定的函數進行斷定,返回true或false

every針對全部當全部元素知足條件爲true,任意一個不知足返回false,  some與every想法只要有一個知足便可

// every()  some()
let a = [1, 2, 3, 4, 5, 6, ];
console.log(a.every(function (value){return value > 10}));  // false  判斷條件爲a數組對象中全部元素都大於10
console.log(a.every(function (value){return value > 0}));   // true   判斷條件爲a數組對象中全部蒜素都大於0

console.log(a.some(function (value) { return value > 3}));  // true   判斷條件爲a數組中有對象大於3
console.log(a.some(function (value) { return value > 7}));  // false   判斷條件爲a數組中有對象大於7 一個都沒有返回false

reduce()和reduceRight() 指定的函數將數組元素進行組合

這在函數式編程中是常見的操做,也能夠稱爲「注入」和「摺疊」

// reduce 須要兩個參數,第一個是執行簡化操做的函數。第二個(可選)的參數是一個傳遞給函數的起始值
let a = [1, 2, 3, 4, 5,];
console.log(a.reduce(function (x, y){return x+y}, 0));  // 數組求和 15
console.log(a.reduce(function (x, y){return x*y}, 1));  // 數組求和 120
console.log(a.reduce(function (x, y){return (x>y)?x:y;},));  // 數組求最大值 5

/*
reduce() 使用的函數與forEach()和map()使用不一樣,若是指定第二個參數的狀況下也就是其市值狀況下
(x, y)的值x 是每次操做後的累加值, y 就是第一個元素的值 拿第一個舉例
console.log(a.reduce(function (x, y){return x+y}, 0));
第一次操做: x = 0 , y = 1  相加結果爲1
第二次操做: x = 1 , y = 2  這裏須要注意的是此次 x 爲上一次的結果,  y 爲第二個元素依次累加

若是沒有起始值,默認的x爲第一個元素,y 爲第二個元素

*/

reduceRight() 和reduce操做同樣知識如今開始順序從右到左

indexOf()和lastindexOf()

獲取元素索引,indexOf從左邊開始查找計算索引值,lastindexOf從右邊開始查找計算索引值

let a = [0, 1, 2, 1, 0];
console.log(a.indexOf(1));  // 從左邊開始找元素值爲1的下標爲: 1
console.log(a.lastIndexOf(1));  // 從右邊開始找元素值爲1的下標爲: 3
// 無論從哪一個方向找第一次出現的元素下標順序一直是從左到右

console.log(a.indexOf(3));  // 從左邊開始查找元素爲3的小標爲:值不存在返回-1

函數

函數定義

函數名稱、圓括號(參數)、花括號{實際執行的操做}

若是函數掛載在一個對象上,做爲對象的屬性,它就稱之爲對象方法。

參數:形參(parameter)和實參(argument),形參至關函數中定義的變量,實參是在運行時的函數調用時傳入的參數

函數調用

凡是沒有形參的均可以省略圓括號

let a = new Object();
let a = new Object;

間接調用

JavaScript中函數也是對象,和其餘JavaScript對象沒什麼兩樣,函數對象也能夠包含方法,其中兩個方法call()和apply()能夠用來間接地調用函數

函數的形參和實參

ECMAScript6中支持默認參數了

// 支持默認參數
function testFunc(x = 1, y = 2){
    this.x = x;
    this.y = y;
    console.log(this.x, this.y)
}

testFunc();

將對象屬性用做實參

相似python中傳字典的key:value

// 定義一個函數接收指定參數
function arrayCopy(/* array */ from, /* index*/ from_start = 0 ,
                   /* array */ to, /* index*/ to_start = 0 ,
                   /* integer */ length) { /* 邏輯代碼*/
}

// 定義一個函數接收對象屬性
function easyCopy(args){
    arrayCopy(args.from,
              args.from_start,
              args.to,
              args.to_start,
              args.length,
    )
}

let a = [1, 2, 3, 4,] , b = [];
easyCopy({'from': a, 'to': b, 'length': 4});
console.log(a, b);

JavaScript真是一個畸形語言waht's the fuck

類和原型

構造函數和原型對象,參考本文「對象-原型對象」

在JavaScript中類也是一個對象,它的建立是經過原型對象來建立的

 

// 定義一個類 開頭大寫
function Person(x, y) {
    // 實例化兩個對象屬性不共享保存在各自的實例中
    this.x = x;
    this.y = y;
}
// 原型重寫, 這裏面的屬性和方法是共享的
Person.prototype = {
    constructor : Person,  // 指定原型對象,也就是對象建立者爲Person
    personMethod1: function () {
        return 'hello 帥哥'
    },
    'key1': 'v1',
    'key2': 'v2',
};

let person1 = new Person(1, 2);
console.log(person1.personMethod1());

在ES6之後出現了Class可是本質上class能夠看作一個語法糖,只是讓原型更加清晰而已

ES6 的類,徹底能夠看做構造函數的另外一種寫法。 

class Point {
  // ...
}

console.log(typeof Point);  // "function"  本質上是一個函數
console.log(Point === Point.prototype.constructor);  // true  原型

構造函數的prototype屬性,在 ES6 的「類」上面繼續存在。事實上,類的全部方法都定義在類的prototype屬性上面。

class Person{
    constructor(){}
    toValue() {}
    toAdd() {}
}

// 等同於
Person.prototype = {
    constructor(){},
    toValue() {},
    toAdd() {},
};

另外,類的內部全部定義的方法,都是不可枚舉的(non-enumerable)。

與 ES5 同樣,實例的屬性除非顯式定義在其自己(即定義在this對象上),不然都是定義在原型上(即定義在class上)。

//定義類
class Person {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

  addMethod(){
        return '(' + this.x + ', ' + this.y + ')';
  }
}

let person1 = new Person(2, 3);

console.log(person1.toString()); // (2, 3)
console.log(person1.hasOwnProperty('x')); // true
console.log(person1.hasOwnProperty('y'));// true
console.log(person1.hasOwnProperty('toString')); // false
console.log(person1.__proto__.hasOwnProperty('toString')); // true
console.log(person1.__proto__.hasOwnProperty('addMethod')); // true

類的靜態方法和屬性

類至關於實例的原型,全部在類中定義的方法,都會被實例繼承。若是在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。

class Tools{
  static makeWorker(){
    return "Hello World"
  }
}

console.log(Tools.makeWorker());  // 靜態方法能夠直接經過類調用

感受愈來愈像python了,不過JavaScript在升級的過程當中不斷吸收其餘語言的專長也是好事   已入坑

靜態屬性指的是 Class 自己的屬性,即Class.propName,而不是定義在實例對象(this)上的屬性。

class Foo {
}

Foo.prop = 1;
Foo.prop // 1

上面的寫法爲Foo類定義了一個靜態屬性prop

目前,只有這種寫法可行,由於 ES6 明確規定,Class 內部只有靜態方法,沒有靜態屬性。

es7 能夠直接聲明瞭不加this~~

相關文章
相關標籤/搜索