基本類型(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指向了不一樣對象,因此不會影響
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
數組每一項均可以保存任何類型的數據,並且數組大小能夠動態調整,跟隨數據的添加而自動增加
var arr = new Array() // 使用Array構造函數 var arr = [] // 使用數組字面量表示法
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
indexOf()
從頭開始找,lastIndexOf()
從末尾開始找共有五個迭代方法,每一個方法都接受兩個參數:要在每一項上運行的函數和(可選的)運行該函數的做用域對象——影響this的值。傳入這些方法中的函數接收三個參數:數組項的值、該項在數組中的位置和數組對象自己
every()
: 對數組每一項運行給定函數,若該函數對每一項都返回true,則返回truesome()
: 對數組每一項運行給定函數,若該函數對任意一項返回true,則返回truefilter()
: 對數組每一項運行給定函數,返回該函數會返回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(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));
try { tryCode - 嘗試執行代碼塊 } catch(err) { catchCode - 捕獲錯誤的代碼塊 } finally { finallyCode - 不管 try / catch 結果如何都會執行的代碼塊 } // 若是使用了try那麼catch和finally必須選一個
經過
RegExp
類型來支持正則表達式,使用相似Perl的語法來建立正則表達式
var expression = / pattern / flags;
其中模式(pattern)能夠是任何簡單或複雜的正則表達式,每一個正則表達式能夠有一個或者多個標誌(flags),用以標明正則表達式的行爲,匹配模式有如下三個標誌.html
exec()
: 接受一個參數,即要使用模式的字符串前端
exec()
而言即便在模式匹配設置了全局標誌(g),也只會返回第一個匹配項,在不設置時,在同一字符串上屢次調用也只會始終返回第一個匹配項的信息;而設置了,則每次調用都會繼續匹配新的項test()
:接受一個字符串參數,在模式與該參數匹配的狀況下返回true不然返回false函數實際上是對象,每個函數都是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
function factorial(num) { if (num <= 1) { return 1 } else { return num * arguments.callee(num - 1) /* arguments.callee便是factorial函數 */ } } console.log(factorial(10)) // 3628800
function a() { console.log(arguments.callee.caller) // 由於arguments.callee就是函數a,因此arguments.callee.caller等於a.caller } function b() { a() } a() // null 由於是頂層調用 b() // b函數的源代碼
做用都是在特定做用域中調用函數,實際上等於設置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不一樣,不會馬上執行
暫無
toFixed()
: 接受一個參數,表明保留幾位小數,會四捨五入var num = 10 console.log(num.toFixed(2)) // 10.00
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對象,從頭開始匹配,返回第一個匹配項的索引,沒有則返回-1replace()
:接收2個參數,字符串或者正則表達式或者RegExp對象,第二個則是用於替換的字符串或者函數;若第一個參數爲字符串則只會替換第一個子字符串,要想替換全部,則必須是正則表達式,而且指定全局標誌(g);若第二個參數是函數,則給這個函數傳入的三個參數分別是匹配項第一個匹配項,第二個等等,最後兩個參數爲匹配項的位置和原始字符串split()
:將字符串以指定分隔符分割成字符串,分隔符能夠是字符串也能夠是正則表達式或者RegExp對象;也能夠接受第二個參數,用於指定數組大小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()
:這個方法像是一個完整的ECMAScript解析器,它接受一個字符串參數,就是要執行的js代碼字符串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
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原型
[image:DF9C0DF3-83B1-4F0C-8F2B-39C112BA24F6-1188-00000B31D26AD3B2/prototype-1.png]es6
[image:C2050E95-6407-4A59-9B93-D6F7081CD863-1188-00000B3DEE188E44/instanceof-2.png]ajax
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
__proto__
是瀏覽器提供的屬性,就是[[prototype]]
這個隱藏屬性,__proto__
由於不是規範屬性,因此要避免使用;
[[prototype]]
es6中用Object.setPrototypeOf(obj, prototype)函數能夠直接修改一個對象的[[prototype]]
__proto__
,可稱爲隱式原型__proto__
是一個隱藏屬性var obj = {} console.log(obj.__proto__ === Object.prototype)
__proto__
`屬性指向建立該對象的函數的prototype屬性Object.prototype.__proto__ === null
console.log(Function instanceof Function) // true console.log(Function instanceof Object) // true console.log(Object instanceof Function) // true console.log(Object instanceof Object) // true
__proto__
來找,沿着b的prototype來找,若是引用的同一個引用就返回true[image:E4783050-568F-4369-A33E-192AE586FBE7-1188-00000B9545C250D1/instanceof-1.png]
[image:8826A813-C433-43DB-9C3D-CF0964451DDA-1188-00000B97B94D72D0/instanceof-2.png]
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__
鏈向上查找,這就是原型鏈[image:802EAC7F-3F88-4EA9-A120-E988C06FB7B3-1188-00000B9C76399666/prototype-4.png]
[image:7231EB7A-F0A5-482E-9632-8C88F3FA7749-1188-00000BA5D0FE85B9/prototype-5.png]
[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]
- 在這裏對函數表達式和普通變量賦值同樣,而函數聲明則馬上賦值了
準備工做完成的事
js在執行代碼片斷前都會進行準備工做來生成‘執行上下文’,代碼片斷分三種狀況:全局代碼、函數體、eval代碼
普通變量(包含函數表達式) | 聲明(默認賦值爲undefined) |
函數聲明 | 賦值 |
this | 賦值 |
參數 | 賦值 |
arguments | 賦值 |
自由變量的取值做用域 | 賦值 |
分四種情況
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
全局和調用普通函數
[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
理解原型對象
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
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
與工廠模式同樣,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也沒什麼意義
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) // 貓科動物
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
瀏覽器對象模型,提供了獨立於瀏覽器顯示內容而與瀏覽器窗口進行交互的對象
//假設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)
: 載入新頁面不會留下歷史記錄window.history.lenght
: 存儲的記錄數history.forward()
: 前進history.back()
: 後退history.go()
: 0從新載入當前頁面; 正值前進幾個頁面; 負值後退幾個頁面screen.width,screen.height
: 桌面分辨率,總大小screen.availWidth,screen.availHeight
: 除去操做系統菜單欄(例如windows的任務欄)之外的區域大小這幾個並不屬於ECMAScript,而是BOM方法
定時器也是BOM方法
文檔對象模型,將xml或html文檔解析成樹形節點的方法
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);
var forms = document.forms[0]; // var user = forms.elements[0]; 和下行同樣 var user = forms.user; console.log(user); user.value = 'admin'; // <input name='user' /> 標籤裏有了admin
addEventListener(even, function, boolean)
: 第一個參數爲事件名不加on,第二個參數爲函數,第三個爲布爾值默認爲false在冒泡階段執行,true在捕獲階段執行removeEventListener()
: 該方法與上一個參數相同,它是移除監聽器;但如果第二個參數爲匿名函數則移除不了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);
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);
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
let [a, b, c] = [1, 'a', ['c']]; console.log(a, b, c); // 1, 'a', ['c']
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];
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)轉換成數組
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則是保持一致,不管參數多少都做爲數組元素*/
參數:
[1, 2, 3].includes(2) // true
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
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
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接口的對象可使用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
用於取代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!
var s1 = Symbol('foo'); var s2 = Symbol('foo'); var a = { [s1]: '123456', [s2]: '789456' }; console.log(a[s1], a[s2]);
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遍歷順序是按照插入順序
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
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 // 雖然值相同可是內存地址不一樣,不是同一個對象
set遍歷順序是按照插入順序
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方法能夠恢復執行. */
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
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
let obj = { *myGenerator() { // ... } };
異步編程的最終解決方案
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對象有如下兩個特色:
(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
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"]
競速模式,誰先完成,以誰爲準執行回調
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 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方法是類的默認方法,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 的行爲保持一致 */
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 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
Object.getPrototypeOf()
方法能夠用來從子類上獲取父類 ,所以, 可使用這個方法判斷, 一個類是否繼承了另外一個類原生構造函數是指語言內置的構造函數, 一般用來生成數據結構。 ECMAScript的原生構造函數大體有下面這些Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()之前, 這些原生構造函數是沒法繼承的 ;Es6中能夠繼承這些
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 Bar { static gey() { console.log(123); } } class Baz extends Bar {} Baz.gey(); // 123 // 無靜態屬性,只能以下添加 Baz.ps = 456; console.log(Baz.ps); // 456
<script type="module" src="foo.js"></script>
// 寫法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只是一個值,不是接口。 */
// main.js import {firstName, lastName, year} from './profile'; function setName(element) { element.textContent = firstName + ' ' + lastName; } import { lastName as surname } from './profile';
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));
使用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();