JavaScript彙總

前端筆記/JavaScript

基本

數據類型

6種數據類型

  • 基本類型: Undefined、Null、Boolean、String、Number
  • 複雜類型: Object

基本類型與引用類型的區別

基本類型

基本類型(String、Boolean、Number、null、undefined),是按值訪問,能夠操做保存在變量中的實際值
  • 基本類型的值不可變
var name = 'jobs'
console.log(name.toUpperCase()) // 輸出JOBS
console.log(name) // 輸出jobs
name.age = 20
name.say = function() {……}
console.log(name.age) // undefined
console.log(name.say) // undefined
// 其中name的實際值並未變化,toUpperCase()方法是返回了一個新的字符串,也不能對基本類型添加屬性和方法,代表了基本類型的值不可變
  • 基本類型的比較是值的比較,只有在值相等時才相等
  • 基本類型的變量存放在棧區
var name = 'jobs'
var age = 20
棧區
name age
jobs 20
棧區包含了變量的標識符和值
  • 基本類型複製
var a = 10
var b = a
a++
console.log(a, b) // 11 10
// 基本類型在複製操做後變量是相互獨立的互不影響

引用類型

就是除了基本類型外的Object類型
  • 引用類型值能夠變,能夠有屬性和方法
var person = {} // 建立個控對象 --引用類型
person.name = 'jozo'
person.age = 22
person.sayName = function(){console.log(person.name)} 
person.sayName() // 'jozo'
delete person.name // 刪除person對象的name屬性
person.sayName() // undefined

// 引用類型的值是同時保存在棧內存和堆內存中的對象
棧區內存保存變量的標識符和指向堆內存中該對象的指針也就是該對象在堆內存中的地址
var person1 = {name:'jozo'}
var person2 = {name:'xiaom'}
var person3 = {name:'xiaoq'}
棧區 堆區
person1 堆內存地址1 ——> Object1
person2 堆內存地址2 ——> Object2
person3 堆內存地址3 ——> Object3
引用類型比較是引用的比較
var p1 = {} 
var p2 = {}
console.log(p1 == p2) // false
// 引用類型是按引用訪問的,因此是比較它們的堆內存中地址是否相同
對象引用
var a = {} // 新建空對象
var b = a // 賦值後a和b指向同一個空對象
a.name = 'jobs'
b.age = 20
console.log(a.name, a.age, b.name, b.age) // jobs 20 jobs 20
// 引用類型的複製是對象保存在棧區的堆內存的地址的複製,兩個變量實際上指向同一個對象,所以任何操做都會互相影響

傳遞參數

  • 全部函數的參數都是按值傳遞
var p1 = {
    name: 'Vian'
}
var setName = function(obj) {
    obj.name = 'jobs'
    return obj
}
var res = setName(p1)
console.log(p1.name) // jobs
console.log(res.name) // jobs

// 把p1傳入setName()中,obj和p1指向同一個對象,修改obj的name值實際上是修改了它們共同指向的對象的name值
var p = {
    name: 'alice'
}

var set = function(ot) {
    ot = {}
    ot.name = 'tom'
    return ot
}

var re = set(p)
console.log(p.name) // alice
console.log(re.name) // tom

// 在函數內部ot新定義了一個對象,obj引用了一個局部對象,外部p和內部ot指向了不一樣對象,因此不會影響

引用類型詳解

Object類型
  • new操做符跟隨Object構造函數
var person = new Object()
person.name = 'alice'
  • 對象字面量表示法
var person = {
    name: 'alice',
    'age': 20
}
屬性名也能使用字符串

訪問對象屬性通常使用點語法,但js中也能使用方括號語法,而且可使用變量訪問屬性javascript

alert(person.name) // alice
  alert(person['name']) // alice
  var personName = 'name'
  alert(person[personName]) // alice
Array類型
數組每一項均可以保存任何類型的數據,並且數組大小能夠動態調整,跟隨數據的添加而自動增加
建立數組的方法
var arr = new Array() // 使用Array構造函數

var arr = [] // 使用數組字面量表示法
length屬性
  • length總會返回大於0的整數,並且它不是隻讀屬性
  • 當設置length的值小於當前的數組長度時,會從末尾開始刪減元素
  • 當設置length爲大於當前數組長度時,則會從末尾新增項,新增每一項的值都爲undefined
檢測數組
  • 對只有一個全局做用域而言,使用instanceof操做符即可以
if (arr instanceof Array) {
    // 對數組的操做
}

// instanceof操做符問題在於,它假定單一的全局執行環境。若網頁中包含不一樣框架,那麼實際上會有兩個以上不一樣的全局執行環境,從而存在兩個以上不一樣的Array構造函數
  • Array.isArray()方法來判斷
if (Array.isArray(arr)) {
      // 對數組的操做
  }
數組經常使用方法
  • 棧方法:
  • push(): 可接受任意個參數,逐個添加到數組末尾,並返回修改後數組長度
  • pop(): 從數組末尾移除最後一項,減小length的值,而後返回移除的項
  • 隊列方法:
  • unshift(): 在數組前面添加任意個項,並返回新數組的長度
  • shift(): 移除數組第一個項,減小length的值,並返回移除的項
  • 排序方法:
  • reverse(): 反轉數組項的順序
  • sort(): 默認升序排列,可接收一個比較函數做爲參數,比較函數有兩個參數,若第一個參數要在第二個參數以前,則返回負數,相等則返回0,若第一個參數要在第二個參數以後,則返回正數
> sort()會調用每項的toString()轉型方法,比較字符串,即便是數值也會轉型後比較字符串,所以[0, 1, 5, 10, 15]使用這個方法會變成[0, 1, 10, 15, 5]
  • 操做方法
  • concat(): 可接收數組、等做爲參數,將數組合並
var color1 = ['pink', 'yellow']
var color2 = color1.concat('skyblue', ['black', 'orange'])
console.log(color1) // pink,yellow
console.log(color2) // pink,yellow,skyblue,black,orange

* `join()`: 可接受一個參數做爲分隔符,將數組轉換爲字符串

* `slice()`: 基於當前數組建立一個新數組,接受一個或者兩個參數,起始位置和結束位置,不會影響當前數組。

> 若只有一個參數,則返回從該參數位置開始到末尾全部的項
> 若兩個參數則返回起始位置到結束位置之間的項,但不包括結束位置
  • splice(): 主要做用向數組中插入項,始終返回一個數組,數組爲原數組中刪除的項,若沒有則返回一個空數組,能夠有三種用途
- 刪除:刪除任意數量的項,接受2個參數:要刪除的第一項的位置和要刪除的個數
- 插入:在指定位置插入任意數量的項,接受3個參數:起始位置、0(要刪除的項數、要插入的項),若是要插入多個項,能夠再傳入第四個,五個,任意個項
- 替換: 能夠在任意位置插入任意項,同時刪除任意項,接受3個參數:起始位置、要刪除的項數、要插入的項
  • 位置方法
  • indexOf()lastIndexOf()php

    • 都接受2個參數:要查找的項和(可選)表示查找起點位置的索引,indexOf()從頭開始找,lastIndexOf()從末尾開始找
    • 都返回要查找的項在數組中的位置,沒找到則返回-1,而且查找時是使用全等操做符(===)
  • 迭代方法
共有五個迭代方法,每一個方法都接受兩個參數:要在每一項上運行的函數和(可選的)運行該函數的做用域對象——影響this的值。傳入這些方法中的函數接收三個參數:數組項的值、該項在數組中的位置和數組對象自己
  • every(): 對數組每一項運行給定函數,若該函數對每一項都返回true,則返回true
  • some(): 對數組每一項運行給定函數,若該函數對任意一項返回true,則返回true
  • filter(): 對數組每一項運行給定函數,返回該函數會返回true的項組成的數組
  • map(): 對數組每一項運行給定函數,返回每次函數調用結果組成的數組
  • forEach(): 對數組每一項運行給定函數,沒有返回值
以上方法都不會修改數組中的包含的值
var numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]

// every
var a1 = numbers.every(function(item, index, array) {
    return item > 2
})
console.log(a1) // false

// some
var a2 = numbers.some(function(item, index, array) {
    return item > 8
})
console.log(a2) // true

// filter
var a3 = numbers.filter(function(item, index, array) {
    return item % 2 === 0
})
console.log(a3) // [2, 4, 6, 8, 0]

// map
var a4 = numbers.map(function(item, index, array) {
    return item * 2
})
console.log(a4) // [2, 6, 10, 14, 18, 4, 8, 12, 16, 0]

// forEach
/*
沒有返回值和for循環迭代數組同樣
*/
  • reduce
reduce(function(total, currentValue [,currentIndex]) [,initialValue]): 是一種數組運算,一般用於將數組的全部成員"累積"爲一個值。
- total:必選,初始值或者累積變量
    - currentValue:必選,當前元素
    - currentIndex:可選,當前元素的索引
    - initialValue:可選,傳遞給函數的初始值
- 基本用法
/**
* reduce(function(total, currentValue, currentIndex) [,initialValue]) 用於數組計算,一般用於將數組累積爲一個值
*/

/**
* 參數tmp是累積變量,item爲當前數組成員,index爲數組下標;每次運行時item會累加到tmp,最後輸出tmp
*/
let arr = [1, 2, 3, 4, 5, 6];

let sum = arr.reduce((tmp, item, index) => tmp + item);

console.log(sum); // 21
- map是reduce的特例
/*
 *map是reduce的特例
 累積變量的初始值能夠是一個數組,例子中初始值是一個空數組,結果返回一個新的數組,等同於map操做;map操做均可以用reduce,因此map是reduce的特例
 */
let handler = (newArr, x) => {
    newArr.push(x + 1);
  return newArr;
}

let arr2 = arr.reduce(handler, []);
console.log(arr2); // [ 2, 3, 4, 5, 6, 7 ]
數組排序常見方法
var arr = [1, 10, 3, 8, 5, 9, 4, 6];

/*冒泡排序*/
    function pops(arr) {
        for (var i = 0; i < arr.length; i++) {
            for (var j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    var temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        return arr;
    }

    console.log(pops(arr));

/*快速排序*/
    function quick(arr) {
        if (arr.length <= 1) {
            return arr;
        }

        var len = Math.floor(arr.length / 2);
        var aj = arr.splice(len, 1);
        var left = [];
        var right = [];

        for (var i = 0; i < arr.length; i++) {
            if (arr[i] < aj) {
                left.push(arr[i]);
            } else {
                right.push(arr[i]);
            }
        }

        return quick(left).concat(aj, quick(right));
    }

    console.log(quick(arr));
Date類型
Error類型
  • try/catch/finally
try {
    tryCode - 嘗試執行代碼塊
}
catch(err) {
    catchCode - 捕獲錯誤的代碼塊
} 
finally {
    finallyCode - 不管 try / catch 結果如何都會執行的代碼塊
}

// 若是使用了try那麼catch和finally必須選一個
RegExp類型
概述
經過 RegExp類型來支持正則表達式,使用相似Perl的語法來建立正則表達式
var expression = / pattern / flags;
  • 其中模式(pattern)能夠是任何簡單或複雜的正則表達式,每一個正則表達式能夠有一個或者多個標誌(flags),用以標明正則表達式的行爲,匹配模式有如下三個標誌.html

    • g:全局模式,模式將被用於整個字符串而非在發現第一個匹配項時當即中止
    • i:不區分大小寫模式,即在匹配時忽略大小寫
    • m:多行模式,即在到達一行文字末尾時還會繼續查找下一行中是否存在與模式匹配的項
實例方法
  • exec(): 接受一個參數,即要使用模式的字符串前端

    • 返回包含第一個匹配項的信息的數組,額外附帶2個屬性index和input,index表示匹配項在字符串中的位置,input表示應用正則表達式的字符串,若沒有匹配項返回null
    • 對於exec()而言即便在模式匹配設置了全局標誌(g),也只會返回第一個匹配項,在不設置時,在同一字符串上屢次調用也只會始終返回第一個匹配項的信息;而設置了,則每次調用都會繼續匹配新的項
  • test():接受一個字符串參數,在模式與該參數匹配的狀況下返回true不然返回false
Function 類型
函數實際上是對象,每個函數都是Function的實例,所以函數名實際上也是一個指向函數對象的指針,不會與函數綁定
函數聲明與函數表達式

解析器對函數聲明和函數表達式有差異對待java

  • 解析器會先讀取函數聲明,並使其在執行任何代碼前可用(能夠訪問);至於函數表達式,則必須等到解析器執行到所在代碼行纔會真正被解析
// 函數聲明
console.log(sum(10, 10)) // 20

function sum(a, b) {
    return a + b
}

// 函數表達式
console.log(add(10, 10)) // 報錯

var add = function(a, b) {
    return a + b
}
  • 函數聲明,在代碼開始執行前,解析器經過一個名爲函數聲明提高的過程,讀取並將函數聲明添加到執行環境中,並放到源代碼樹的頂端,所以能夠先調用再定義
  • 函數表達式,函數位於一個初始化語句中,也就是在執行到函數代碼前,變量不會保存有對函數的引用
函數內部屬性
在函數內部有兩個特殊對象arguments和this
  • arguments是一個類數組對象,保存着函數的全部參數;它還有一個callee屬性,它是一個指針,指向擁有這個arguments對象的函數
  • callee
function factorial(num) {
    if (num <= 1) {
        return 1
    } else {
        return num * arguments.callee(num - 1)
        /* arguments.callee便是factorial函數 */
    }
}

console.log(factorial(10)) // 3628800
  • caller,es5中定義的函數對象屬性,返回一個調用當前函數的引用,若是是由頂層調用的話,則返回null
function a() {
    console.log(arguments.callee.caller) 
      // 由於arguments.callee就是函數a,因此arguments.callee.caller等於a.caller
}

function b() {
    a()
}

a() // null 由於是頂層調用
b() // b函數的源代碼
  • this,this引用的是(this的值)函數據以執行的環境對象,在網頁全局做用域中調用函數時候this的引用對象就是window
函數的屬性和方法
  • 每一個函數都包含兩個屬性:length和prototype
  • length: 形參個數
  • prototype: 原型
  • 每一個函數包含兩個非繼承而來的方法: apply()和call();
做用都是在特定做用域中調用函數,實際上等於設置this的值
  • apply():接收2個參數,一個是在其中運行的函數的做用域,一個是Array實例或者arguments對象
  • call():接收2個參數,一個是在其中運行的函數的做用域,其他參數要直接傳遞給函數,也就是逐一列舉
function sum1(a, b) {
    return a + b
}

function sum2(a, b) {
    var s1 = sum1.apply(this, arguments) // arguments對象
    var s2 = sum1.apply(this, [a, b]) // Array實例
    console.log(s1, s2)
}

sum2(10, 10) //20 20

function sum3(a, b) {
    return sum1.call(this, a, b) // 參數逐一傳入
}

console.log(sum3(10, 10)) // 20
apply()call(),真正的用武之地並不在於傳遞參數,而是擴充函數賴以運行的做用域
var color = 'blue'
var o = {
    color: 'pink'
}

function sayColor() {
    console.log(this.color)
}

/* this指向了全局做用域window */
sayColor() // blue
sayColor(this) // blue
sayColor(window) // blue

/* this指向了o */
sayColor.apply(o) // pink
sayColor.call(o) // pink
  • bind(): 它會建立一個函數的實例,其this的值會被綁定到傳給bind函數的值
var colors = 'skyblue'
var c = {
    colors: 'orange'
}

function say() {
    console.log(this.colors)
}

var say2 = say.bind(c)
say2() // orange
bind與apply、call不一樣,不會馬上執行
Boolean類型
暫無
Number類型
  • toFixed(): 接受一個參數,表明保留幾位小數,會四捨五入
var num = 10
console.log(num.toFixed(2)) // 10.00
String類型
string包含length屬性,返回字符串長度
字符串方法
  • 字符方法

charAt()charCodeAt():這兩個方法都接收一個參數,即基於0的字符的位置.其中charAt()以單字符字符串的形式返回給定位置的字符,而charCodeAt()則是返回編碼node

var stringTXT = 'hellow'
console.log(stringTXT.charAt(3)) // l
console.log(stringTXT.charCodeAt(3)) // 108
  • 字符串操做方法
  • concat():用於將一個或多個字符串拼接起來
  • slice(),substr(),substring():這三個方法都返回被操做字符串的子字符串,也都接受一或兩個參數,第一個參數指定開始位置,第二個指定結束位置;slice和substring的第二個參數指定的是子字符串的最後一個字符後面的位置,substr的第二個參數則是指定返回字符的個數
var txt = 'abcdefg'
console.log(txt.slice(1, 3)) // bc
console.log(txt.substring(1, 3)) // bc
console.log(txt.substr(1, 3)) // bcd
  • 位置方法

indexOf()lastIndexOf():這兩個方法接受2個參數,第一個爲要查找的字符串,(可選)第二個表示查找起點位置的索引,都是從字符串中尋找指定的字符串,而後返回該字符串的位置,若沒有則返回-1,一個從頭開始找,一個從末尾開始找jquery

  • 大小寫轉換
  • toLowerCase()toUpperCase():所有轉換成小寫,大寫
  • toLocaleLowerCase()toLocaleUpperCase():所有轉換成小寫,大寫,這是根據特定地區來轉換大小寫,在不知道代碼運行在哪一個語言的狀況下使用
  • 字符串的模式匹配方法
  • match():接受一個參數,參數爲正則表達式或者RegExp對象,返回一個數組,數組保存着每個匹配的子字符串
var txt = 'abcdefgabcdefgabcdefg'
var reg = /ab/gi
console.log(txt.match(reg)) // ['ab', 'ab', 'ab']
  • search():接受一個參數,參數爲正則表達式或者RegExp對象,從頭開始匹配,返回第一個匹配項的索引,沒有則返回-1
  • replace():接收2個參數,字符串或者正則表達式或者RegExp對象,第二個則是用於替換的字符串或者函數;若第一個參數爲字符串則只會替換第一個子字符串,要想替換全部,則必須是正則表達式,而且指定全局標誌(g);若第二個參數是函數,則給這個函數傳入的三個參數分別是匹配項第一個匹配項,第二個等等,最後兩個參數爲匹配項的位置和原始字符串
  • split():將字符串以指定分隔符分割成字符串,分隔符能夠是字符串也能夠是正則表達式或者RegExp對象;也能夠接受第二個參數,用於指定數組大小

內置單體對象

Global對象
URI編碼方法
  • encodeURI():主要用於整個uri,它不會對自己屬於uri的特殊字符進行編碼如冒號、正斜槓、問好、井號
  • encodeURIComponent():主要用於對其中一段進行編碼,它會對任何非標準字符進行編碼
var uri = 'http://www.who.com/search?wd=123'
console.log(encodeURI(uri))
// 結果: http://www.who%08.com/search?wd=123
console.log(encodeURIComponent(uri))
// 結果: http%3A%2F%2Fwww.who%08.com%2Fsearch%3Fwd%3D123
encodeURIComponent使用的比encodeURI多
  • decodeURI():解碼方法,只能對encodeURI()編碼的進行解碼
  • decodeURIComponent():解碼方法,能夠解碼全部字符
eval
  • eval():這個方法像是一個完整的ECMAScript解析器,它接受一個字符串參數,就是要執行的js代碼字符串
Math對象
經常使用屬性
  • Math.PI: π的值
經常使用方法
  • 求極值
  • min(),max():求最小,最大值;能夠接受任意個參數
  • 舍入方法
  • ceil():向上求整
  • floor():向下求整
  • round():四捨五入
  • 隨機值
  • random(): 返回一個0-1之間的隨機數,但不包含0和1,從區間內取一個值,能夠用一下公式
(max - min) * Math.random() + min
  • 絕對值
  • abs(): 絕對值
  • 次方
  • pow(a, n): 第一個爲底,第二個參數爲次方值,a的n次方

原型和閉包

對象

(undefined,number,string,boolean)屬於簡單的值類型,不是對象。函數、數組、對象、null、構造函數都是對象,都是引用類型判斷一個變量是否是對象,值類型用typeof,引用類型用instanceof
  • 在js中數組、函數、對象都是對象,對象裏面一切都是屬性,只有屬性,沒有方法; 方法也是一種屬性
  • 對象都是經過函數建立的,函數是對象
function F() {
  this.name = 'jobs'
}

var f1 = new F() // 對象能夠經過函數建立
var arr = [1, 2, 3] // 語法糖,本質是下面的簡寫

var arr = new Array() // Array是構造函數
arr[0] = 1
arr[1] = 2
arr[2] = 3

原型

prototype原型
  • 函數默認有一個prototype屬性,prototype屬性的值是一個對象(屬性的集合),默認只有一個constructor屬性指向這個函數自己

[image:DF9C0DF3-83B1-4F0C-8F2B-39C112BA24F6-1188-00000B31D26AD3B2/prototype-1.png]es6

  • 如圖所示,superType是一個函數,右邊是原型
  • 原型做爲對象,屬性的集合,能夠自定義許多屬性,例如Object,裏面包含了其餘幾個屬性

[image:C2050E95-6407-4A59-9B93-D6F7081CD863-1188-00000B3DEE188E44/instanceof-2.png]ajax

  • 能夠在自定義的方法的prototype中新增本身的屬性

function Fn() {正則表達式

Fn.prototype.name = '王福鵬'
Fn.prototype.getYear = function() {
    return 1988
}

}

[image:E2D062D3-B7D0-457C-97D2-6607E729A1C5-1188-00000B470B9F9F19/prototype-3.png]

function Fn() {
    Fn.prototype.name = '王福鵬'
    Fn.prototype.getYear = function() {
        return 1988
    }
}

var f1 = new Fn()
console.log(f1.name) // 王福鵬
console.log(f1.getYear()) // 1988
console.log(f1.__proto__ === Fn.prototype) // true
  • 上述代碼,f1對象是從Fn中new出來的,這樣f1就能夠調用Fn中的屬性,由於每一個對象都有一個隱藏屬性'__proto__',這個屬性引用了建立這個對象的函數的prototype,因此f1.__proto__ === Fn.prototype,這裏的'__proto__'成爲隱式原型

隱式原型

__proto__是瀏覽器提供的屬性,就是 [[prototype]]這個隱藏屬性, __proto__由於不是規範屬性,因此要避免使用;
  • es5中用Object.getPrototypeOf(obj)函數得到一個對象的[[prototype]]
  • es6中用Object.setPrototypeOf(obj, prototype)函數能夠直接修改一個對象的[[prototype]]

    • 參數obj將被設置原型的對象.
    • prototype該對象新的原型(能夠是一個對象或者null).
  • 每一個對象都有一個__proto__,可稱爲隱式原型
  • __proto__是一個隱藏屬性
var obj = {}
console.log(obj.__proto__ === Object.prototype)
  • 有上述代碼可知:每一個對象都有一個__proto__`屬性指向建立該對象的函數的prototype屬性
  • 有一個特例Object.prototype.__proto__ === null

instanceof 操做符

console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
  • instanceof判斷規則是,沿着a的__proto__來找,沿着b的prototype來找,若是引用的同一個引用就返回true

[image:E4783050-568F-4369-A33E-192AE586FBE7-1188-00000B9545C250D1/instanceof-1.png]

  • 即instanceof表示的就是一種繼承關係,或者原型鏈的結構

[image:8826A813-C433-43DB-9C3D-CF0964451DDA-1188-00000B97B94D72D0/instanceof-2.png]

繼承

  • JavaScript的繼承是用原型鏈來體現的
function Foo() {}
var f1 = new Foo()

f1.a = 10

Foo.prototype.a = 100
Foo.prototype.b = 200

console.log(f1.a, f1.b) // 10 200
上述代碼,f1是Foo函數new出來的對象,f1.a是f1對象的基本屬性,而f1.b是從Foo.prototype得來的,由於 f1.__proto__指向Foo.prototype
  • 訪問一個對象的屬性時,如今基本屬性中查找,若是沒有,就沿着__proto__鏈向上查找,這就是原型鏈
  • 如何區分一個屬性是基本的仍是從原型中找到的,用hasOwnProperty

[image:802EAC7F-3F88-4EA9-A120-E988C06FB7B3-1188-00000B9C76399666/prototype-4.png]

  • f1中沒有hasOwnProperty方法,它是從Object.prototype而來的

[image:7231EB7A-F0A5-482E-9632-8C88F3FA7749-1188-00000BA5D0FE85B9/prototype-5.png]

  • 對象的原型鏈都會找到Object.prototype,所以全部對象都有Object.prototype的方法,這就是繼承

執行上下文

  • 上下文環境

[image:B0C9A9CF-FB04-4776-A2E5-AA72796DC573-1188-00000BA936740391/this-1.png]

- 第一句報錯a未定義,二三句a爲undefined,說明在js代碼一句句執行前,瀏覽器就已經開始一  些準備工做,其中就包括對變量的聲明,而不是賦值,賦值操做是在運行到語句時才執行;

[image:2185D116-A4C9-41BC-BF30-92F3E57EC3B3-1188-00000BAD8E05AD77/this-2.png]

- 這是第一種狀況

[image:DCC314B3-40D8-4F45-8DC1-72AB6A089648-1188-00000BB1A9090280/this-3.png]

- 第一種狀況是對變量只聲明,未賦值;第二種直接賦值給了this;這也是‘準備工做’之一

[image:88C73D7D-40A0-470F-81BE-B2651D40A767-1188-00000BB70805B00A/this-4.png]

- 在這裏對函數表達式和普通變量賦值同樣,而函數聲明則馬上賦值了
  • 準備工做完成的事

    • 變量、函數表達式 —— 變量聲明,默認賦值爲undefined
    • this —— 賦值
    • 函數聲明 —— 賦值
    • 這三種數據準備狀況就是‘執行上下文’或者‘執行上下文環境’
  • js在執行代碼片斷前都會進行準備工做來生成‘執行上下文’,代碼片斷分三種狀況:全局代碼、函數體、eval代碼

    • 在函數中,arguments和函數參數會直接賦值;函數每調用一次,都會產生一個新的執行上下文環境;並且函數在定義的時候(不是調用的時候),就已經肯定函數體內部自由變量的做用域
    • 在執行代碼以前,把將要用到的全部的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空
  • 全局代碼的上下文環境爲
普通變量(包含函數表達式) 聲明(默認賦值爲undefined)
函數聲明 賦值
this 賦值
  • 若是代碼片斷是函數體,則在此基礎上附加
參數 賦值
arguments 賦值
自由變量的取值做用域 賦值

this

  • 函數中this究竟是什麼值,是在函數真正被調用時肯定的,函數定義時肯定不了;this永遠指向的是最後調用它的對象,也就是看它執行的時候是誰調用的;由於this取值是執行上下文環境的一部分,每次調用都會產生一個新的執行環境上下文
  • 分四種情況

    • 構造函數
function F(num) {
    this.num = num
    this.log = 'txt'
    console.log(this)
  }
- 把函數做爲構造函數,那麼this就是new出來的對象
- 函數做爲對象的一個屬性
-  若是函數做爲對象的一個屬性,而且做爲對象的屬性被調用時,this指向該對象
var obj = {
    x: 10,
    fn: function() {
        console.log(this) // {x:10, fn: function}
        console.log(this.x) // 10
    }
  }
  obj.fn()
- 函數調用call和apply,bind時調用
- 當一個函數被call和apply調用時,this的值就取傳入的對象的值
var obj = {
      x: 10
  }

  function fn() {
      console.log(this)
      console.log(this.x)
  }

  fn.call(obj) // {x: 10} 10
  fn.apply(obj) // {x: 10} 10
  var fns = fn.bind(obj)
  fns() // {x: 10} 10
  • 全局和調用普通函數

    • this指向window

做用域

  • 做用域

[image:C6407A7F-8B78-4A86-8C15-6697981F687C-1188-00000BFD4FA9B6C0/this-5.png]
[image:08320C13-AE3E-4C60-A615-25442AD209CA-1188-00000BFECEDB8EC1/this-6.png]

  • 做用域中變量的值是在執行過程當中產生的肯定的
  • 若是要查找一個做用域下某個變量的值,就須要找到這個做用域對應的執行上下文環境,再在其中尋找變量的值

自由變量和做用域鏈

  • 要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,切記切記——其實這就是所謂的「靜態做用域」
  • 做用域鏈

[image:12DFA8C1-973E-4680-A97D-C57F33ABEBCB-1188-00000C050C4391C4/this-7.png]

面向對象的程序設計

屬性類型

包含兩種數據屬性和訪問器屬性

數據屬性

數據屬性包含一個數據值的位置,在這個位置能夠讀取和寫入值,數據屬性有4個描述其行爲的特性
  • [[Configurable]]:表示可否經過delete刪除屬性,從而從新定義屬性,可否修改屬性特性,可否把屬性修改成訪問器屬性
  • [[Enumerable]]:可否經過for-in循環返回屬性
  • [[Writable]]:可否修改屬性值
  • [[Value]]:包含這個屬性的數據值,默認undefined
直接在對象上定義的屬性,它們的 [[Configurable]]、[[Enumerable]]、[[Writable]]的值默認爲true,[[Value]]爲指定值
var p = { name: 'nico' } // [[Value]]的值爲 nico
要修改屬性默認特性,必須使用es5的Object.defineProperty()方法,接收三個參數,屬性所在對象,屬性名字和一個描述符對象。其中描述符對象屬性必須是:configurable、enumerable、writable、value.設置其中一個或多個值(小寫)

能夠屢次調用Object.defineProperty(),可是Configurable一旦設置爲false(不可配置),就不能再將其設置爲true(可配置),並且爲false時其餘屬性也受到限制了

在調用Object.defineProperty()時,若不設置,configurable、enumerable、writable的值都爲false

var Person = {}
Object.defineProperty(Person, 'name', {
    configurable: false,
    wirtable: false,
    value: 'alice'
})

console.log(Person.name) // alice

delete Person.name

console.log(Person.name) // alice

Person.name = 'Nico'

console.log(Person.name) // alice

Object.defineProperty(Person, 'name', {
    configurable: true,
    value: 'Nico'
})

console.log(Person.name) // 報錯

訪問器屬性

訪問器屬性不包含數據值,包含一對getter和setter函數(不過它們不是必須的),在讀取訪問器屬性時,會調用getter函數,它負責返回有效的值;在寫入訪問器屬性時會調用setter函數,它負責處理數據;訪問器有以下4個特性

* [[Configurable]]:表示可否經過delete刪除屬性,從而從新定義屬性,可否修改屬性特性,可否把屬性修改成訪問器屬性

  • [[Enumerable]]:可否經過for-in循環返回屬性
  • [[Get]]:在讀取時調用的函數,默認值爲undefined
  • [[Set]]:在寫入屬性時調用的函數,默認值爲undefined
訪問器屬性不能直接定義,必須使用Object.defineProperty()來定義
var book = {
     _year: 2014, // _ 是一種經常使用標記,用於表示只能經過對象方法訪問的屬性
     edition: 1
 }

 Object.defineProperty(book, 'year', {
     get: function() {
         return this._year
     },
     set: function(val) {
         if (val > 2014) {
             this._year = val
             this.edition = val - 2014
         }
     }
 })

 book.year = 2016
 console.log(book.edition) // 2
這是使用訪問器屬性的常見方式,即設置一個屬性的值會致使其餘屬性發生變化

不必定要同時使用getter和setter,只指定getter表示屬性不能寫,嘗試寫入屬性會被忽略;只指定setter表示不能讀,不然會返回undefined;在嚴格模式下都會拋出錯誤

定義多個屬性

Object.defineProperties(),es5中定義的,能夠經過描述符一次定義多個屬性,接收2個對象參數,第一個是要添加和修改其屬性的對象,第二個對象的屬性與第一個對象中要添加或修改的屬性要一一對應
var book = {}
  Object.defineProperties(book, {
      _year: {
          value: 2000
      },

      edition: {
          writable: true,
          value: 1
      },

      year: {
          get: function() {
              return this._year
          },

          set: function(val) {
              if (val > 2000) {
                  this._year = val
                  this.edition += val - 2000
              }
          }
      }
  })

  console.log(book.year) // 2000
  book.year = 2017
  console.log(book.edition) // 18

讀取屬性的特性

Object.getOwnPropertyDescriptor(),能夠取得給定屬性的描述符;接收2個參數:屬性所在對象和要讀取的其描述符的屬性名,返回值爲一個對象;若是是數據屬性,這個對象的屬性有configurable、enumerable、writable、value;若是是訪問器屬性,這個對象的屬性有configurable、enumerbale、get、set

建立對象

工廠模式

function createperson(name, age) {
      var o = {}
      o.name = name
      o.age = age
      o.say = function() {
          console.log(this.name, this.age)
      }
      return o
  }

  var p1 = createperson('alice', 18)
  var p2 = createperson('lulu', 20)

  console.log(p1 instanceof Object) // true
  console.log(p1 instanceof createperson) // false
工廠模式解決了建立多個類似對象的問題,但沒有解決對象識別問題(怎麼知道一個對象的類型)

構造函數模式

function Person(name, age) {
      this.name = name
      this.age = age
      this.say = function() {
          console.log(this.name, this.age)
      }
  }

  var p1 = new Person('alice', 18)
  var p2 = new Person('lulu', 20)

  console.log(p1 instanceof Object) // true
  console.log(p1 instanceof Person) // true
與工廠模式對比,沒有顯示的建立對象,直接將屬性和方法賦給了this對象,沒有return語句

在這個模式下建立的全部對象既是Object的實例又是Person的實例

建立自定義構造函數意味着能夠將它的實例標識爲一種特定類型,這正是構造函數模式賽過工廠模式的地方

使用構造函數會使得每一個方法都要在每一個實例上建立一遍,在上述例子中p1和p2都有say方法,但那不是同一個Fuction的實例,由於在js中函數是對象,會致使不一樣的做用域鏈和標識符解析

console.log(p1.say == p2.say) // false

原型模式

建立的函數都有一個prototype(原型)屬性,它是一個指針,指向一個對象,這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法,也就是prototype就是經過調用構造函數而建立的那個對象實例的原型對象
function Person() {}
Person.prototype.name = 'nico'
Person.prototype.say = function() {
    console.log(this.name)
}

var p1 = new Person()
p1.say() // nico
console.log(p1 instanceof Person) // true
  • 理解原型對象

    • 只要建立了新函數,就會爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象
    • 默認狀況下,全部原型對象都會自動得到一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針,例如前面的代碼中Person.prototype.constructor指向Person;經過這個構造函數能夠繼續爲原型對象添加其它屬性和方法
    • 建立了自定義構造函數後,其原型對象默認只會取得constructor屬性;至於其餘方法,則從Object繼承而來
    • 當調用一個構造函數建立一個新的實例後,該實例內部包含一個指針(內部屬性),指向構造函數的原型對象;es5中這個指針叫[[Prototype]],在腳本中沒有標準的方式訪問[[Prototype]],但Firefox,Safari,Chrome在每一個對象上都支持一個屬性__proto__;這個連接存在於實例與構造函數的原型對象之間,而不是實例與構造函數之間
    • 能夠經過對象實例訪問保存在原型中的值,但卻不能重寫原型中的值
    • 原型模式最大問題是由其共享的本性所致使的,改變一個實例的屬性,全部實例都會變
  • 更簡單的原型語法
function Person() {}
Person.prototype = {
    name: 'nico',
    say: function() {
        console.log(this.name)
    }
}

var p1 = new Person()
p1.say() // nico
console.log(p1 instanceof Person) // true
console.log(p1.constructor == Person) // false
  • 以對象字面量形式建立,會致使constructor屬性再也不指向Person
  • 能夠設置constructor爲適當值
function Person() {}
  Person.prototype = {
      constructor: Person,
      name: 'nico',
      say: function() {
          console.log(this.name)
      }
  }
  var p1 = new Person()
  p1.say() // nico
  console.log(p1 instanceof Person) // true
  console.log(p1.constructor == Person) // true
但這樣會致使constructor的[[Enumerable]]爲true,原生的constructor爲不可枚舉屬性,這時候要用es5的Object.defineProperty()重寫constructor屬性
function Person() {}
    Person.prototype = {
        constructor: Person,
        name: 'nico',
        say: function() {
            console.log(this.name)
        }
    }

    Object.defineProperty(Person.prototype, 'constructor', {
        enumerable: false,
        value: Person
    })

    var p1 = new Person()
    p1.say() // nico
    console.log(p1 instanceof Person) // true
    console.log(p1.constructor == Person) // true

組合模式 - 最經常使用方式

  • 概述

    • 組合使用構造函數模式和原型模式

      • 這是建立自定義類型最多見的模式
      • 構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性
      • 這樣,每一個實例都有一份本身的實例屬性的副本,同時又共享着對方法的引用,還支持向構造函數傳參
      • 這種模式是目前使用最普遍的一種建立自定義類型的方法,能夠說是用來定義引用類型的一種默認模式
function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype = {
    constructor: Person,
    say: function() {
        console.log(this.name, this.age)
    }
}

var p1 = new Person('alice', 20)
var p2 = new Person('nico', 30)
p1.say()
console.info(p1.name == p2.name) // false
console.info(p1.say == p2.say) // true

動態原型模式

  • 爲了解決獨立的構造函數和原型,動態原型模式將全部信息封裝到構造函數中,而經過在構造函數中初始化原型,又保持了同時使用構造函數和原型的優勢;也就是經過檢測某個應該存在的方法是否有效來決定是否初始化原型
function Person(name, age) {
      this.name = name
      this.age = age
      if (typeof this.say != 'function') {
          Person.prototype.say = function() {
              console.log(this.name, this.age)
          }
      }
  }

  var p1 = new Person('alice', 20)
  var p2 = new Person('nico', 30)
  console.info(p1.name == p2.name) // false
  console.info(p1.say == p2.say) // true
  • 這裏只有在say方法不存在時,纔會將它添加到原型中,這段代碼只有在初次調用構造函數時纔會執行
  • if語句檢查可使初始化以後應該存在的任何屬性或方法,沒必要一堆if語句來檢測每一個屬性和每一個方法,只要檢查其中一個便可,還可使用instanceof來肯定類型

寄生構造函數模式

與工廠模式同樣,instanceof操做符不能用來肯定對象類型,在能使用其餘模式的狀況下不推薦使用

穩妥構造函數模式

function Person(name, age) {
      var o = {}
      o.say = function() {
          console.log(name)
      }
      return o
  }

  var p1 = Person('alice', 20)
  p1.say() // alice
這樣變量Person中保存的是一個穩妥對象,除了調用say()外,沒法訪問其數據成員,這種模式instanceof也沒什麼意義

繼承

借用構造函數

  • 使用call或者apply,將父對象的構造函數綁定在子對象上
function Animal() {
      this.species = '貓科動物'
  }

  function Cat(name, color) {
      Animal.apply(this, arguments)
      this.name = name
      this.color = color
  }

  var cat1 = new Cat('kit', 'white')
  console.log(cat1.species) // 貓科動物
  • 方法都在構造函數中定義,函數沒法複用,借用構造函數不多單獨使用

原型鏈

  • 使用prototype屬性,若是子構造函數的prototype對象指向一個父構造函數的實例,那麼全部子構造函數的實例均可以繼承父構造函數了
function Animal() {
      this.species = '貓科動物'
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  Cat.prototype = new Animal()

  Cat.prototype.constructor = Cat

  var cat1 = new Cat('ly', 'blue')
  console.log(cat1.species) // 貓科動物
  • 解析上述代碼

    • Cat.prototype = new Animal()

將Cat的prototype對象指向一個Animal的實例,它至關於刪除了prototype原先的值,而後賦予一個新值

- `Cat.prototype.constructor = Cat`任何一個prototype都有一個constructor屬性,因爲上述代碼,會致使Cat.prototype.constructor指向Animal,須要從新將構造函數定義回Cat
- 若是替換了prototype對象那麼下一步一定是將constructor指向原來的構造函數
* 因爲不能在不影響全部對象實例的狀況下傳參和因爲原型中包含的引用類型值得問題,不多會單獨使用原型鏈

原型式繼承(用空對象作中介)

function Animal() {
      this.species = '貓科動物'
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  function F() {}

  F.prototype = new Animal()
  Cat.prototype = new F()

  var cat1 = new Cat('ly', 'blue')

  console.log(cat1.species) // 貓科動物
  console.log(Animal.prototype.constructor) // Animal的源代碼
function Animal() {
      this.species = '貓科動物'
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  function extend(child, parent) {
      var F = function() {}
      F.prototype = new parent()
      child.prototype = new F()
      child.prototype.constructor = child
      child.uber = parent.prototype
  }

  extend(Cat, Animal)

  var cat1 = new Cat('jack', 'orange')

  console.log(cat1.species) // 貓科動物
這個extend函數,就是YUI庫如何實現繼承的方法

最後一行只是爲了實現繼承的完備性,純屬備用性質

寄生繼承

  • 建立一個僅用於封裝繼承過程的函數
  • 沒法實現函數複用
function creatAnother(obj) {
      var clone = Object(obj)
      clone.say = function() {
          console.log('hi')
      }
      return clone
  }

  var Person = {
      name: 'nico'
  }

  var an = creatAnother(Person)
  an.say() // hi

組合繼承 - 最經常使用方式

  • 也叫僞經典繼承,將原型鏈和借用構造函數的技術整合一塊兒,從而發揮兩者之長
  • 經過原型鏈實現對原型屬性和方法的繼承,經過借用構造函數來實現對實例屬性的繼承
  • 最經常使用方式
  • 函數可複用,可傳參,不存在引用屬性共享問題
function Person(name, age) {
      this.name = name
      this.age = age
  }

  Person.prototype.sayName = function() {
      console.log(this.name)
  }

  Person.prototype.sayAge = function() {
      console.log(this.age)
  }

  function Girls(name, age) {
      Person.apply(this, arguments)
  }

  Girls.prototype = new Person()
    Girls.prototype.constructor = Girls;

  var alice = new Girls('alice', 16)
  alice.sayName() // alice
  alice.sayAge() // 16

寄生組合式繼承

  • 最好的繼承方式
function inter(Super, Sub) {
      var clone = Object(Super.prototype) // 建立對象
      clone.constructor = Sub // 加強對象
      Sub.prototype = clone // 指定對象
  }

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

  Person.prototype.say = function() {
      console.log(this.name, this.age)
  }

  function Girls(name, age, play) {
      Person.apply(this, arguments)
      this.play = play
  }

  inter(Person, Girls)

  Girls.prototype.plays = function() {
      console.log(this.play)
  }

  var alice = new Girls('alice', 16, 'game')
  alice.say() // alice 16
  alice.plays() // game
  • inter函數實現了寄生組合的最簡單形式,在函數內部先建立超類原型副本,爲建立的副本添加constructor屬性,最後將副本賦給子類型的原型

瀏覽器環境

DOM和BOM

BOM

瀏覽器對象模型,提供了獨立於瀏覽器顯示內容而與瀏覽器窗口進行交互的對象

location

  • 用於存儲當前頁面URL信息的對象
  • location的屬性以下:
//假設url爲 http://search.phpied.com:8080/search?p=javascript#results
    location.href = "http://search.phpied.com:8080/search?p=javascript#results";
    location.hash = "";
    location.host = "search.phpied.com:8080";
    location.hostname = "search.phpied.com";
    location.pathname = "/search";
    location.port = "8080";
    location.protocol = "http:";
    location.search = "?p=javascript";
  • location的三個方法:

    • reload(): 無參數,從新載入當前頁面; location = location也能用於從新載入當前頁面
    • assign(newURL): 載入新頁面會留下歷史記錄
    • replace(newURL): 載入新頁面不會留下歷史記錄

histroy

  • 存儲了頁面的訪問記錄
  • window.history.lenght: 存儲的記錄數
  • history.forward(): 前進
  • history.back(): 後退
  • history.go(): 0從新載入當前頁面; 正值前進幾個頁面; 負值後退幾個頁面

screen

  • 提供了桌面信息
  • screen.width,screen.height: 桌面分辨率,總大小
  • screen.availWidth,screen.availHeight: 除去操做系統菜單欄(例如windows的任務欄)之外的區域大小

alert,prompt,confirm

這幾個並不屬於ECMAScript,而是BOM方法

定時器

定時器也是BOM方法

DOM

文檔對象模型,將xml或html文檔解析成樹形節點的方法

DOM節點

DOM節點屬性
  • nodeName : 節點的名稱
  • nodeValue :節點的值
  • nodeType :節點的類型
DOM訪問快捷方式
  • getElementsByTagName、getElementsByName、getElementById
  • 能夠用屬性形式訪問attribute,由於class爲保留字,因此class要用className訪問
兄弟節點、body元素、首尾子節點
  • 屬性:nextSibling與previousSibling,獲得下一個兄弟節點、上一個兄弟節點;空行也算
  • 屬性:nextElementSibling 與previousElementSibling,獲得下一個上一個兄弟元素節點
修改節點
  • innerHtml,可用於修改節點內容
  • style屬性用於修改樣式
建立節點
  • document.createElement(): 建立元素節點
  • document.createTextNode: 建立文本節點
克隆節點
  • cloneNode(): 該方法有一個參數,true深拷貝,包括全部子節點, false淺拷貝,只針對當前節點
var odv = document.getElementsByTagName('div')[0];
var p2 = document.getElementsByTagName('p')[1];
odv.appendChild(p2.cloneNode(false));
odv.appendChild(p2.cloneNode(true));
// 淺拷貝,文本節點不會拷貝
插入節點
  • appendChild(newChild): 將節點插入到 節點的子節點列表的末尾
  • insertBefore(newChild, refChild): 將節點插入節點指定子節點的前面
var odv = document.getElementsByTagName('div')[0];
var p2 = document.getElementsByTagName('p')[1];
odv.appendChild(p2.cloneNode(true));
odv.insertBefore(p2.cloneNode(true), document.getElementsByTagName('p')[0]);
移除、替換節點
  • removeChild(child): 從子節點列表中移除指定節點
  • replaceChild(newChild, oldChild): 將指定子節點替換成新節點
var odv = document.getElementsByTagName('div')[0];
var p2 = document.getElementsByTagName('p')[1];
odv.replaceChild(document.createElement('li'), p2);

只適用於HTML的DOM方法

訪問文檔的基本方式
  • document.body
  • document.images, 至關於Core DOM組件的document.getElementsByTagName('img')的調用
  • document.forms, 獲取所form,包含子元素;而後能夠經過elements來訪問子元素,若是form或者子元素有名字,也可用過名字訪問
var forms = document.forms[0];
// var user = forms.elements[0]; 和下行同樣
var user = forms.user;
console.log(user);
user.value = 'admin';
// <input name='user' /> 標籤裏有了admin
CookiesTitleReferrerDomain

事件

DOM的事件監聽

  • addEventListener(even, function, boolean) : 第一個參數爲事件名不加on,第二個參數爲函數,第三個爲布爾值默認爲false在冒泡階段執行,true在捕獲階段執行
  • removeEventListener(): 該方法與上一個參數相同,它是移除監聽器;但如果第二個參數爲匿名函數則移除不了

捕捉和冒泡

  • 捕捉:事件先發生在document上,依次傳遞給body等,最終達到該元素上
  • 冒泡:事件先發生在該元素上,再依次向上,而後到body,直至document對象上

阻斷事件傳播

  • stopPropagation(): 這樣就使得事件沒法傳播了,只發生在本身身上
var op = document.getElementsByTagName('p');
op[0].addEventListener('click', function(e) {
    console.log(e.target);
    e.stopPropagation();
}, false);

阻止默認事件

  • pereventDefault(): 不是全部默認事件都能阻止
var alink = document.getElementsByTagName('a');
alink[0].addEventListener('click', function(e) {
    e.preventDefault();
    console.log(123);
}, false);

跨瀏覽器事件監聽(兼容IE)

DOM2屬性 IE對應屬性 說明
addEventListener attachEvent 事件監聽
event window.event IE中的全局事件對象
target srcElement 事件元素的target屬性
stopPropagation cancelBubble only-IE屬性,設置爲true
preventDefault returnValue only-IE屬性,設置爲false
removeEventListener detachEvent 移除事件監聽

編程模式與設計模式

編程模式

行爲隔離

就是HTML、CSS和JS分開

命名空間

爲了減小命名衝突,一般減小全局變量的使用;更好的方法是將不一樣變量和方法定義在不一樣命名空間中;本質是隻定義一個全局變量,並將其它變量和方法定義爲該變量的屬性

將對象用做命名空間

// 新建全局變量MYAPP
var MYAPP = MYAPP || {};

// 添加屬性
MYAPP.event = {};

// 添加方法
MYAPP.event = {
  getEvent: function(e) {
    // ......
  },
  // ......other methods
}

命名空間中構造器應用

咱們能夠在命名空間中使用構造器函數
// 本例中,咱們用Element構造器建立一個dom元素
var MYAPP = MYAPP || {};
MYAPP.dom = {};
MYAPP.dom.Element = function(type, props) {
    var tmp = document.createElement(type);
    for (var i in props) {
        tmp.setAttribute(i, props[i]);
        console.log(i, props[i]);
    }
    return tmp;
}

var a = new MYAPP.dom.Element('a', {
    href: "javascript.void(0);"
});

document.body.appendChild(a);

namespace方法

var MYAPP = {};

MYAPP.namespace = function(str){
    var parts = str.split("."),
    parent = MYAPP,
    i=0,
    l=0;

    if(parts[0]==="MYAPP"){
        parts = parts.slice(1);
    }
    for(i=0,l=parts.length; i<l;i++){
        if(typeof parent[parts[i]] === "undefined"){
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
    }
    return parent;
}

設計模式

設計模式有23種甚至更多,下面爲4種經常使用模式

單例模式

工廠模式

裝飾器模式

觀察者模式

var sub = {
    callbacker: [],
    // 發佈
    add: function(fn) {
        this.callbacker.push(fn);
    },
   // 訂閱
    fire: function(fn) {
        this.callbacker.forEach(function(element) {
            element();
        });
    }
}

sub.add(function() {
    console.log(1)
});
sub.add(function() {
    console.log(2)
});

sub.fire(); // 1 2

ES6

let和const

let

  • let用於申明變量,而且只在該代碼塊內有效
  • let不存在變量提高,只能先聲明再使用
  • 暫時性死區,在代碼塊內未使用let申明變量前,變量都是不可用的
  • 不容許重複聲明變量
  • let和const實際上爲js提供了塊級做用域

const

  • const就是常量
  • 一旦聲明,必須就賦值,且不可變動
  • 沒法重複聲明

全局對象的屬性

  • 全局對象是最頂層的對象,在瀏覽器環境中是window對象,在nodeJS中是global對象
  • es5中全局對象屬性與全局變量等價
  • es6中var,function命令聲明的全局變量依然是全局對象的屬性;而let、const、class命令聲明的全局變量則再也不是全局對象的屬性

變量的解構賦值

數組的結構賦值

基本用法

let [a, b, c] = [1, 'a', ['c']];
console.log(a, b, c); // 1, 'a', ['c']
  • 即匹配模式,等號兩邊格式同樣,便可進行變量的賦值
  • 若右邊表達式不是數組,則會報錯;只要有Iterator接口的數據結構均可以使用數組形式的解構賦值
  • 適用於var、let、const命令
  • 若解構失敗變量值爲undefined

默認值

  • 解構賦值容許有默認值
  • ES6內部使用嚴格相等運算符(===),因此一個數組成員不嚴格等於undefined是不會使用默認值的
var [a = 1, b = 2, c = 3, d = 4] = [undefined, null, 'c', d];
console.log(a, b, c, d); //1 null "c" 4

對象的解構賦值

var {x, y} = {x: 1, y: 'a' };
    console.log(x, y); //1 "a"

    
    var { bar: baz } = { bar: 'hello' };
    console.log(baz); //hello


    /*下面例子中js會將{}當成代碼塊,此時須要用小括號括起來*/
    var k;
    // {k} = {k: 123};會報錯,js會將{}當成代碼塊,此時須要用小括號括起來
    ({ k } = { k: 123 });
    console.log(k); // 123


    /*在這個例子中,loc是模式不是變量不會賦值*/
    var local = {
        loc: {
            ui: 'ui',
            txt: 'txt'
        },
        title: '標題'
    }

    var {
        loc: {
            ui,
            txt
        },
        title
    } = local;
    // console.log(loc); loc is not defined
    console.log(ui, txt, title); // ui txt 標題


    /*對象的解構賦值也能設置默認值,一樣只有在值嚴格爲undefined時,默認值纔會有用*/
    var {bat = 123, bas = 456, bad} = {bat: undefined, bas: null, bad: 'bad'};
    console.log(bat, bas, bad); // 123 null "bad"


    /*若解構失敗變量值爲undefined*/


    /*對象的解構賦值,能夠方便的將對象方法賦值到某個變量中*/
    var {pow, random} = Math;
    // 將Math對象的次方和隨機數方法賦值給了pow和random
    console.log(pow(2, 3)); // 8
    console.log(random()); // 隨機數


    /*由於數組也是特殊對象,所以數組也能進行對象的解構賦值*/
    var arr = [1, 2, 3];
    var {0: first, [arr.length - 1]: last} = arr;
    console.log(first, last); // 1 3

字符串的解構賦值

// 將字符串轉成了相似數組的對象
const [a, b] = 'hi';
console.log(a, b); // h i

// 字符串有length屬性,所以能夠對這個對象進行解構賦值
var {
    length: len
} = 'hello';
console.log(len); // 5

函數參數的解構賦值

function add([x, y]) {
        return x + y;
    }
    console.log(add([1, 2])) // 3

    function move({
        x = 0,
        y = 0
    } = {}) {
        return [x, y];
    }
    console.log(move({
            x: 3
        })) // [3, 0]
    console.log(move({
            y: 8
        })) // [0, 8]
    console.log(move({
            x: 3,
            y: 8
        })) // [3, 8]

解構賦值的應用

  • 交換變量值
[x, y] = [y, x];
  • 從函數返回多個值
  • 函數參數定義和設置默認值
  • 遍歷Map結構
  • 輸入模塊的指定方法
  • 提取JSON
var data = {
        id: 1,
        name: 'admin',
        type: 0,
        data: {
            goods: [9001, 9002],
            goods_type: [0, 1]
        }
    }

    var {
        id,
        name,
        type,
        data: {
            goods,
            goods_type
        }
    } = data;
    console.log(id, name, type, goods, goods_type); // 1 "admin" 0 (2) [9001, 9002] (2) [0, 1]

數組的擴展

新增方法

Array.from

Array.from方法用於將相似數組的對象和可遍歷對象(包含set、map)轉換成數組
  • 實際應用中能夠將DOM操做返回的NodeList和函數內arguments轉爲數組
  • 還能接受第二個參數,做用相似於數組的map方法,對每一個元素進行處理,將處理後的值放入返回的數組

Array.of

  • 用於將一組值轉換爲數組
var arr1 = Array();
var arr2 = Array(3);
var arr3 = Array(1, 2);
console.log(arr1); // []
console.log(arr2); // [ , , ]
console.log(arr3); // [1, 2]

var arr4 = Array.of(1);
console.log(arr4); // [1]

/*由於數組的構造函數,只有在參數不小於2時纔會做爲數組值,一個參數時做爲數組長度;Array.of則是保持一致,不管參數多少都做爲數組元素*/

copyWithin

  • copyWithin(target, start, end): 將指定位置的成員複製到其餘位置(會覆蓋原有成員),而後返回當前數組,也就是會修改當前數組
  • 參數:

    • target(必須): 從該位置開始替換
    • start(可選):從該位置讀取,默認爲0,若爲負值則爲倒數
    • end(可選):讀取到該位置結束,默認爲數組長度,若爲負值則爲倒數

find和findIndex

  • 參數爲一個函數,將數組內元素依次執行該回調函數,找出第一個返回值爲true的成員,find是返回這個成員,若沒有符合條件的成員則返回undefined;findIndex是返回該成員位置,若沒有符合條件的成員則返回-1
  • 回調函數,接收三個參數:當前值、當前位置、原數組

fill

  • 用給定值,填充一個數組,會覆蓋原有所有值
  • 能夠接受第2、三個參數,用於指定填充的起始和結束位置

數組實例的keys、values、entries

  • 都用於遍歷數組,返回一個遍歷器,可用for...of來遍歷
  • keys()是對鍵名,values()是對鍵值,entries()是對鍵值對

數組實例的includes

  • includes返回一個布爾值,表示數組是否包含給定的值
  • 該方法可選第二個參數,表明起始搜索位置,默認爲0,若爲負值則倒數
[1, 2, 3].includes(2) // true

函數擴展

函數的默認值

  • 能夠設置函數默認值,有默認值的參數最好做爲尾參
  • 可以使用rest參數, 即...參數
function add(...val) {
        let sum = 0;
        for (let i of val) {
            sum += i
        }
        console.log(sum);
    }

    add(1, 2, 3, 4, 5, 6, 7, 8, 9); //45
  • rest參數是一個數組,可使用任意數組方法
  • rest參數後面不能再跟任何參數
  • rest參數能夠替代Array.prototype.slice.call(arguments)

擴展運算符

基本含義

  • 擴展運算符就是三個點...,用於將數組轉爲用逗號分隔的參數序列
function add(...val) {
        let sum = 0;
        for (let i of val) {
            sum += i
        }
        console.log(sum);
    }

    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

    add(...arr); //45

用於替代數組apply方法

  • 例如將數組追加另外一個數組末尾
var arr1 = [1, 2, 3];
    var arr2 = [4, 5, 6];
    // es5
    Array.prototype.push.apply(arr1, arr2);
    // es6
    // arr1.push(...arr2);
    console.log(arr1); // [1, 2, 3, 4, 5, 6]

擴展運算符的應用

合併數組

// es5
    var arr1 = [1, 2, 3]
    var arr2 = arr1.concat([4, 5, 6], 7);
    console.log(arr2); // [1, 2, 3, 4, 5, 6, 7]

    // es6
    var arr3 = ['a', 'b', 'c'];
    var arr4 = [...arr3, ...['e', 'f', 'g'], 'h'];
    console.log(arr4); //["a", "b", "c", "e", "f", "g", "h"]

實現了Iterator接口的對象

任何實現了Iterator接口的對象均可以使用擴展運算符轉爲真正的數組,對於沒有Iterator接口的對象可使用Array.from

箭頭函數

基本用法

var f = () => 5;
    /*
    就等於
    var f = function() {
        return 5
    }
    */
    var five = f();
    console.log(five); // 5

    var f1 = v => v;
    /*
    就等於
    var f1 = function(v) {
        return v
    }
    */
    console.log(f1(10)); // 10

    /*代碼塊部分語句多餘一條,那麼就須要用大括號括起來,而且用return返回*/
    var sum = (num1, num2) => num1 + num2;
    var sum2 = (num1, num2) => {
        return num1 + num2
    };
    console.log(sum(10, 20), sum2(10, 20)); // 30 30

    // 若要返回一個對象,那麼就要將對象用小括號括起來
    var getuser = id => ({
        id: id,
        temp: 'user'
    });
    console.log(getuser(01)); // {id: 1, temp: "user"}

    /*和解構賦值相結合*/
    var full = ({
        user,
        id
    }) => 'id is ' + id + ', user is ' + user;
    var us = {
        id: 1,
        user: 'admin'
    };
    console.log(full(us)); // id is 1, user is admin

箭頭函數使用注意點

  • 函數體內this對象就是定義時的對象而不是使用時的對象
  • 不能夠當作構造函數,也就是不能使用new命令,不然會報錯
  • 不可使用arguments對象,該對象不在函數體內,若要用,可使用rest參數代替
  • 不可使用yield命令,所以箭頭函數不能用做Generator函數

函數綁定(ES7)

用於取代call、apply、bind
  • 寫法: 對象::函數
foo::bar; // 等同於 bar.bind(foo);
foo::bar(...arguments) // 等同於 bar.apply(foo, arguments);
  • 若左邊爲空,右邊爲一個對象的方法,就等因而將該方法綁定到該對象上
let log = ::console.log;
// 等同於 var log = console.log.bind(console);

對象的擴展

屬性簡潔表示

  • 屬性簡寫
/*es6中容許只寫屬性名,不寫屬性值;這時屬性值等於屬性名所表明的變量*/
    var foo = 123;
    var baz = {
        foo
    };
    console.log(baz); // {foo: 123}
    /*等同於
    var baz = {foo: foo}
    */
  • 方法簡寫
/*es6中方法也能簡寫*/
    var o = {
            say() {
                return 'hi!'
            }
        }
        /*等同於
        var o = {
            say: function() {
                return 'hi!'
            }
        }
        */
    console.log(o.say()); // hi!

Symbol類型

var s1 = Symbol('foo');
    var s2 = Symbol('foo');
    var a = {
        [s1]: '123456',
        [s2]: '789456'
    };

    console.log(a[s1], a[s2]);

Set和Map數據結構

Set

基本用法

ES6提供了新的數據結構Set,它相似於數組,可是成員值都是惟一的,沒有重複的值
var s = new Set();
[1, 2, 2, 3, 3, 4, 5, 6].map(x => s.add(x));
console.log(...s); // 1 2 3 4 5 6
console.log(s.size); // 6
  • Set自己是一個構造函數用來生成set結構數據
  • Set函數能夠接收一個數組或者類數組的對象做爲參數
  • Set能夠簡單的實現數組去重

Set實例的屬性和方法

Set實例方法分爲操做方法和遍歷方法
屬性
  • size: set對象的個數
操做方法
  • add(value): 添加某個值,返回Set結構自己
  • delete(value): 刪除某個值,返回一個布爾值
  • has(value): 返回一個布爾值,表示該值是不是set成員
  • clear(): 清除全部成員,沒有返回值
遍歷方法
set遍歷順序是按照插入順序
  • keys(): 返回鍵名遍歷器
  • values(): 返回鍵值遍歷器
  • entries(): 返回鍵值對遍歷器
  • foreach(): 使用回調函數遍歷每一個成員

Map

基本用法和目的

js鍵值對中以往只能用字符串當作鍵;ES6提供了Map數據結構,它和對象相似也是鍵值對,可是它可使用任意類型的值當作鍵
var m = new Map();
var o = {
    name: 'admin',
    id: 1
};
m.set(o, 'content');
console.log(m.get(o)); // content
console.log(m.has(o)); // true
console.log(m.delete(o)); // true
console.log(o.name); // admin

/*能夠接受數組當參數*/
var mo = new Map([
    ['first', 10],
    [true, 'foo']
]);
console.log(mo.get('first'), mo.get(true)); // 10 foo
  • 對同一個鍵屢次賦值,後面的賦值會覆蓋前面的
  • 只有對同一個對象的引用,Map纔會將它當作同一個鍵
var mo = new Map();
var k1 = ['a'];
var k2 = ['a'];
mo.set(k1, 111).set(k2, 222);
console.log(mo.get(k1), mo.get(k2)); // 111 222
mo.set(['b'], 333);
console.log(mo.get(['b'])); // undefined
// 雖然值相同可是內存地址不一樣,不是同一個對象
  • 如果鍵是簡單類型,那麼只要兩個值嚴格相等,那麼就視爲同一個鍵,對於NaN,雖然NaN和本身不相等,但map將它視作同一個鍵

屬性和方法

實例的屬性和操做方法
  • size: 獲得成員數量
  • set(k, v): 設置成員,返回整個map結構,若鍵已經存在,則會更新鍵值;由於set返回自身,因此能夠採用鏈式寫法
  • get(key): 讀取key對應的值,若不存在返回undefined
  • has(key): 返回布爾值,表示鍵是否存在Map結構數據當中
  • delete(key): 刪除某個鍵,成功返回true,失敗返回false
  • clear(): 清除全部成員,沒有返回值
遍歷方法
set遍歷順序是按照插入順序
  • keys(): 返回鍵名遍歷器
  • values(): 返回鍵值遍歷器
  • entries(): 返回鍵值對遍歷器
  • foreach(): 遍歷Map每一個成員

Generator函數

  • Generator函數爲es6提供一種異步編程解決方案
  • Generator函數是一個狀態機,封裝了多個內部狀態
  • 執行Generator函數會返回一個遍歷器對象,也就是說Generator除了是狀態機仍是一個遍歷器對象生成器
  • Generator使用時function關鍵字與函數名之間要加一個星號;函數內部使用yield(英文是產出的意思)語句,定義不一樣的內部狀態
function* test() {
    yield 'hello';
    yield 'world';
    return 'end';
}
var t = test();
console.log(t.next()); // {value: "hello", done: false}
console.log(t.next()); // {value: "world", done: false}
console.log(t.next()); // {value: "end", done: true}
console.log(t.next()); // {value: undefined, done: true}

/*
調用Generator函數後, 該函數並不執行, 返回的也不是函數運行結果, 而是一個指向內部狀態的指針對象, 也就是上一章介紹的遍歷器對象(Iterator Object).
下一步, 必須調用遍歷器對象的next方法, 使得指針移向下一個狀態。 也就是說, 每次調用next方法, 內部指針就從函數頭部或上一次停下來的地方開始執行, 直到遇到下一個yield語句(或return語句) 爲止。 換言之, Generator函數是分段執行的, yield語句是暫停執行的標記, 而next方法能夠恢復執行.
*/

next方法的參數

  • yield語句自己沒有返回值,next方法的參數會被當作上一個yield語句的返回值

for...of循環

  • for...of循環能夠自動遍歷Generator函數時生成的Iterator對象, 且此時再也不須要調用next方法
function* foo() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
    return 6;
};
for (let v of foo()) {
    console.log(v);
};
// 1 2 3 4 5
  • 一旦next方法的返回對象的done屬性爲true, for...of循環就會停止, 且不包含該返回對象, 因此上面代碼的return語句返回的6, 不包括在for...of循環之中

yield* 語句

  • 在一個Generator函數中使用另外一個Generator函數
function* foo() {
        yield 1;
        yield 2;
        yield 3;
        yield 4;
        yield 5;
        return 6;
    };

    function* ts() {
        yield 'a';
        yield* foo(); // yield* 語句
        yield 'c';
    };

    for (let v of ts()) {
        console.log(v);
    };
    // a 1 2 3 4 5 c

做爲對象屬性的Generator函數

let obj = {
  *myGenerator() {
    // ...
  }
};
  • 在屬性前面寫一個星號,則表明這個屬性是Generator函數

async await

異步編程的最終解決方案
let show = async() => {
        let a1 = await $.ajax({
            url: './data/arr.txt',
            type: 'GET',
            dataType: 'json'
        })
        let j1 = await $.ajax({
            url: './data/json.json',
            type: 'GET',
            dataType: 'json'
        })
        return [a1, j1]
    }
 
    show().then(v => {
        let [a, j] = v;
        console.log(a, j)
    })

Promise對象

Promise是異步編程的一種解決方案, 比傳統的解決方案——回調函數和事件——更合理和更強大 。Promise對象有如下兩個特色:
(1) 對象的狀態不受外界影響。 Promise對象表明一個異步操做, 有三種狀態: Pending(進行中) 、 Resolved(已完成, 又稱Fulfilled)
和Rejected(已失敗) 。(2) 一旦狀態改變, 就不會再變, 任什麼時候候均可以獲得這個結果。Promise 對象的狀態改變,只有兩種可能:從 Pending 變爲 Resolved 和從 Pending 變爲 Rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對 Promise 對象添加回調函數,也會當即獲得這個結果。

基本

var test = (resolve, reject) => {
        var num = Math.random() * 100;
        setTimeout(() => {
            if (num > 50) {
                // console.log('success');
                resolve('200 ok!');
            } else {
                // console.log('error');
                reject('400 fialt!');
            }
        }, 300)
    }

    var somepromise = new Promise(test).then((result) => {
            console.log('成功' + result);
        }, (err) => {
            // 如有第二個函數失敗後則執行這個,不執行catch
            console.log('err' + err);
        }).catch((error) => {
            console.log('失敗' + error);
        })
        /*
        catch用來指定reject的回調,效果和寫在then的第二個參數裏面同樣;
        不過它還有另一個做用:在執行resolve的回調(也就是上面then中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼並不會報錯卡死js,而是會進到這個catch方法中;
        這與咱們的try/catch語句有相同的功能.
        */

鏈式調用

var p1 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('數據1');
            }, 100);
        })
    }

    var p2 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('數據2');
            }, 100);
        })
    }

    var p3 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('數據3');
            }, 100);
        })
    }

    p1().then((data) => {
            console.log(data);
            return p2();
        }).then((data) => {
            console.log(data);
            return p3();
        }).then((data) => {
            console.log(data);
        })
        // 數據1 數據2 數據3

方法

all

Promise的all方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後才執行回調
var t1 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('t1');
            }, 1000);
        });
    }

    var t2 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('t2');
            }, 600);
        });
    }

    Promise.all([t1(), t2()]).then((data) => {
        console.log('所有完成');
        console.log(data);
    });
    // 所有完成 ["t1", "t2"]

race

競速模式,誰先完成,以誰爲準執行回調
var t1 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('t1');
            }, 1000);
        });
    }

    var t2 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('t2');
            }, 600);
        });
    }

    Promise.race([t1(), t2()]).then((data) => {
        console.log('競速模式');
        console.log(data);
    });
    // 競速模式 t2

Class

基本

class Student {
        constructor(arr) {
            //[name, age, sex]
            [this.name, this.age, this.sex] = arr;
        }
        toString() {
            console.log(this.name, this.age, this.sex);
        }
    }

    var vian = new Student(['vian', 16, 'girl']);
    vian.toString(); // vian 16 girl

    console.log(typeof Student); // function
    console.log(Student === Student.prototype.constructor); // true
    console.log(vian.toString === Student.prototype.toString); // true
  • 類的數據類型就是函數
  • 類自己指向構造函數
  • 在類的實例上調用方法就是調用原型上的方法
  • 類的方法之間不加符號不然會報錯

constructor

constructor方法是類的默認方法,new命令生成實例時自動調用該方法,必須有,若不顯式定義,則會自動添加一個空的constructor方法;默認返回實例對象(this),也能夠指定返回對象

類的實例對象

class Point {
        constructor([x, y]) {
            [this.x, this.y] = [x, y];
        }
        toString() {
            console.log(...[this.x, this.y]);
        }
    }

    var p1 = new Point([10, 20]);
    var p2 = new Point([60, 80]);
    console.log(p1.hasOwnProperty('x')); // true
    console.log(p1.hasOwnProperty('y')); // true
    console.log(p1.hasOwnProperty('toString')); // false
    console.log(p1.__proto__.hasOwnProperty('toString')); // true
    console.log(p1.__proto__ === p2.__proto__); // true

    p1.__proto__.pasName = () => {
        console.log('Oops');
    }

    p1.pasName(); // Oops
    p2.pasName(); // Oops

    /*
    x和y都是實例對象point自身的屬性(由於定義在this變量上) , 因此hasOwnProperty方法返回true, 而        toString是原型對象的屬性(由於定義在Point類上) , 因此hasOwnProperty方法返回false。 這些都與ES5        的行爲保持一致
    */
  • 不能像調用函數同樣調用class,會報錯,要用new生成實例
  • 與ES5同樣, 實例的屬性除非顯式定義在其自己(即定義在this對象上) , 不然都是定義在原型上(即定義在class上)
  • 與ES5同樣, 類的全部實例共享一個原型對象 , 這就意味着能夠經過實例的__proto__屬性爲Class添加方法
  • class不存在變量提高,不會對class關鍵字進行提高,因此必須先定義再使用

class表達式

const myClass = class me {
        getName() {
            console.log(me.name);
        }
    }

    let t1 = new myClass();
    t1.getName(); // me
    console.log(myClass.name); // me
    // console.log(me.name); // me is not defined

    /*
    上面代碼使用表達式定義了一個類。 須要注意的是, 這個類的名字是MyClass而不是Me, Me只在Class的內部代碼可用, 指代當前類。
    若是類的內部沒用到的話, 能夠省略Me, 也就是能夠寫成下面的形式。
    const MyClass = class { //...... };
    */

    // 採用Class表達式, 能夠寫出當即執行的Class
    let person = new class {
        constructor(name) {
            this.name = name;
        }
        say() {
            console.log(this.name);
        }
    }('張三');

    person.say(); // 張三

class的繼承

class Color {
        constructor([x, y, z]) {
            this.arr = [x, y, z];
        }
        getColor() {
            console.log(...this.arr);
        }
    }

    class myColor extends Color {
        constructor([x, y, z], a) {
            super([x, y, z]);
            this.a = a;
        }
        geta() {
            console.log(this.a);
        }
    }

    var c = new myColor(['pink', 'skyblue', 'orange'], 'apple');
    c.geta(); // apple
    c.getColor(); // pink skyblue orange
  • class之間能夠經過extends關鍵字實現繼承
  • 子類必需要在構造函數中使用super方法,它在這裏表示父類的構造函數, 用來新建父類的this對象;這是由於子類沒有本身的this對象, 而是繼承父類的this對象, 而後對其進行加工。 若是不調用super方法, 子類就得不到this對象
  • Object.getPrototypeOf()方法能夠用來從子類上獲取父類 ,所以, 可使用這個方法判斷, 一個類是否繼承了另外一個類

super關鍵字

  • 做爲函數調用時(即super(...args)) , super表明父類的構造函數
  • 做爲對象調用時(即super.prop或super.method()) , super表明父類。 注意, 此時super便可以引用父類實例的屬性和方法, 也能夠引用父類的靜態方法

實例的__proto__屬性

  • 子類實例的__proto__屬性的__proto__屬性, 指向父類實例的__proto__屬性。 也就是說, 子類的原型的原型, 是父類的原型

原生構造函數的繼承

原生構造函數是指語言內置的構造函數, 一般用來生成數據結構。 ECMAScript的原生構造函數大體有下面這些Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()之前, 這些原生構造函數是沒法繼承的 ;Es6中能夠繼承這些

class的get和set

class Test {
        constructor(x) {
            this.val = x;
        }
        set pro(val) {
            this.val = val;
        }
        get pro() {
            return this.val;
        }
    }

    var t = new Test('測試');
    console.log(t.pro); // 測試
    t.pro = 123;
    console.log(t.pro); // 123

Class的靜態方法

class Bar {
        static gey() {
            console.log(123);
        }
    }

    class Baz extends Bar {}
    Baz.gey(); // 123

    // 無靜態屬性,只能以下添加
    Baz.ps = 456;
    console.log(Baz.ps); // 456
  • 靜態方法實例沒法調用,只有類自己調用
  • 靜態方法可以被繼承
  • 靜態方法也能從super上調用
  • Es6沒有靜態屬性

Module

瀏覽器

  • 瀏覽器使用ES6模塊的語法以下
<script type="module" src="foo.js"></script>

export和import

export

// 寫法1
export var one = 1;

// 寫法2
var p1 = 1;
var p2 = 2;
export {p1, p2};
// 可使用as關鍵字輸出別名
export {p1 as pi, p2 as po};

/*
export 1; // 報錯
var m = 1;
export m; // 報錯
上面兩種寫法都會報錯, 由於沒有提供對外的接口。第一種寫法直接輸出1,第二種寫法經過變量m,仍是直接輸出1。1只是一個值,不是接口。
*/

import

// main.js
import {firstName, lastName, year} from './profile';

function setName(element) {
    element.textContent = firstName + ' ' + lastName;
}

import { lastName as surname } from './profile';
  • 使用export命令定義了模塊的對外接口之後, 其餘JS文件就能夠經過import命令加載這個模塊(文件)
  • import命令具備提高效果, 會提高到整個模塊的頭部, 首先執行
foo();
import { foo } from 'my_module';
// 上面的代碼不會報錯, 由於import的執行早於foo的調用

總體輸出

  • 除了指定加載某個輸出值, 還可使用總體加載, 即用星號(*) 指定一個對象, 全部輸出值都加載在這個對象上面
/*circle.js文件, 它輸出兩個方法area和circumference*/
// circle.js
export function area(radius) {
    return Math.PI * radius * radius;
} 
export function circumference(radius) {
    return 2 * Math.PI * radius;
}

/*如今, 加載這個模塊*/
// 逐步加載寫法
// main.js
import { area, circumference } from './circle';
console.log('圓面積: ' + area(4));
console.log('圓周長: ' + circumference(14));

// 總體加載寫法
import * as circle from './circle';
console.log('圓面積: ' + circle.area(4));
console.log('圓周長: ' + circle.circumference(14));

export default

使用import命令的時候, 用戶須要知道所要加載的變量名或函數名, 不然沒法加載。
爲了給用戶提供方便, 讓他們不用閱讀文檔就能加載模塊, 就要用到export default命令, 爲模塊指定默認輸出 。
// export-default.js
export default function () {
    console.log('foo');
}

// import-default.js
import customName from './export-default';
customName(); // 'foo'

/*
模塊文件export-default.js, 它的默認輸出是一個函數
其餘模塊加載該模塊時, import命令能夠爲該匿名函數指定任意名字
上面代碼的import命令, 能夠用任意名稱指向export-default.js輸出的方法, 這時就不須要知道原模塊輸出的函數名。 須要注意的是, 這時import命令後面, 不使用大括號。
*/
export default命令用在非匿名函數前, 也是能夠的
// export-default.js
export default function foo() {
    console.log('foo');
} 
// 或者寫成
function foo() {
    console.log('foo');
}
export default foo;

//    foo函數的函數名foo, 在模塊外部是無效的。 加載的時候, 視同匿名函數加載
//  一個模塊只能有一個默認輸出, 所以export deault命令只能使用一次
//    export default命令其實只是輸出一個叫作default的變量, 因此它後面不能跟變量聲明語句

// 正確
export var a = 1;
// 正確
var a = 1;
export default a;
// 錯誤
export default var a = 1;

// 有了export default命令, 輸入模塊時就很是直觀了, 以輸入jQuery模塊爲例
import $ from 'jquery';

// 若是想在一條import語句中, 同時輸入默認方法和其餘變量, 能夠寫成下面這樣
import customName, { otherMethod } from './export-default';

// 若是要輸出默認的值, 只需將值跟在export default以後便可
export default 42;

// export default也能夠用來輸出類
// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();

Tips

幾種遍歷語法

  • for循環
  • foreach:爲數組提供,可是沒法中途結束,return和break都無效
  • for...in: 遍歷數組鍵名
  • for...of:只有是具備Iterator接口的都能使用,能夠中途結束
相關文章
相關標籤/搜索