1. 背景
1.1. Javascript 誕生于1995年
1.2. Javascript 實現(xiàn)組成部分
- ECMAScript 提供語言核心功能
- BOM 提供與瀏覽器交互的方法和接口
- DOM 提供訪問和操作網(wǎng)頁內容的方法與接口
2. html中的引用
- 內部引用
<head>
<script>
function () {
alert('begin')
}
</script>
</head>
- 外部引用
<script type="text/javascript" src="外部js鏈接"></script>
3. Javascript 基本概念
3.1 語法

3.2 關鍵字和保留字

3.3 變量 (松散型 - 可保存任何類型的數(shù)據(jù))
var 定義變量僅為該變量作用域下的內部變量
function test () {
var message = ''
}
alert (message) // 報錯
3.4 數(shù)據(jù)類型
3.4.1 typeof檢驗數(shù)據(jù)類型

3.4.2 Boolean() 轉換

3.4.3 Number() 轉換

3.4.4 parseInt() 轉換

3.4.5 Object類型

3.4.6 其他知識點
- 保存浮點數(shù)值所需的內容是保存整數(shù)的兩倍
alert(undefined == null) // true
alert(NaN == NaN) // false
3.5 操作符
3.5.1 一元操作符
- ++ / -- (前置后置)
var a = 1
var b = 2
var c = ++a - 2 // 0
var d = a - 2 // 0
var a = 1
var b = 2
var c = a++ - 2 // -1
var d = a - 2 // 0
- (轉數(shù)字) / - (負數(shù))
var a = ‘a’
var c = +a // NaN
var d = '6'
var e = +d // 6
var f = -e // -6
3.5.2 位操作符

3.5.3 布爾操作符

var found = true
var result = found && a // false , a未定義
var result1 = found ||| a // true
3.5.4 乘性操作符
Infinity * 0 = NaN
Infinity * Infinity = Infinity
0 / 0 = NaN
3.5.5 加性操作符
var a = "5" + "5" // 55
var b = 1
var c = 2
var d = 'hahah' + c + b // hahah21
var e = 'haha' + (c + b) // haha3
var f = 5- null // 5
var g = 5 - true // 4
3.5.6 關系操作符
- 字符串按照字母編碼比較
var a = "51" < "5" // true
var b = NaN > 3 // false
var c = NaN <= 3 // false
3.5.7 相等操作符
null == undefined // true
"NaN" == NaN // false
NaN == NaN // false
null == 0 // false
undefined == 0 // false
3.5.8 條件操作符
var a = b ? c : d
3.6 語句
if , do-while , while , for , for-in , break , continue , label , switch
for (var i = 0; i < 10; i++) {
alert(i) // 0-9
}
alert(i) // 10
var num = 0
add:
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
break add
}
num++
}
}
alert(num) // 55
var num = 0
add:
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
continue add
}
num++
}
}
alert(num) // 95
3.7 函數(shù)
3.7.1 參數(shù) arguments
function howManyArgs () {
alert (arguments.length) // 2
}
howManyArgs ("1", "2")
- 函數(shù)沒有簽名,無法重載
- 未指定返回值的函數(shù)返回的是undefined
4. 變量,作用域和內存
4.1 基本類型和引用類型的值
4.1.1 變量
- 基本類型值
var name = 'haha'
name.age = 27
alert(name.age) // undefined
- 引用類型值
var person = new Object()
person.age = 27
alert(person.age) // 27
4.1.2 復制變量
- 基本類型,互不影響
var num1 = 1
var num2 = num1 = 1
- 引用類型,兩個變量引用同一個對象,會改變
var obj1 = new Object()
var obj2 = obj1
obj2.name = 'haha'
alert(obj1.name) // haha 被賦值了
4.1.2 傳遞參數(shù)
- 基本類型 - 保存在棧內存
function addTen (num) {
num += 10
return num
}
var count = 20
var res = addTen(count)
alert(count) // 20
alert(res) // 30
- 引用類型 - 保存在堆內存
function setName(obj) {
obj.name = 'hah'
obj = new Object () // 局部對象在函數(shù)執(zhí)行完后摧毀
obj.name = 'hello'
}
var person = new Object()
setName(person)
alert(person.name) // hah
4.1.3 檢測類型
- typeof 基本類型檢測(見上)
- instanceOf 引用類型檢測
alert(colors instanceOf Array/Object/RegExp)
4.2 執(zhí)行環(huán)境和作用域
4.2.1 當代碼在一個環(huán)境執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈
- 作用域鏈:保證對執(zhí)行環(huán)境有權訪問的所有變量和函數(shù)的有序訪問
4.2.2沒有塊級作用域
a. if語句中的變量聲明會將變量添加到當前的執(zhí)行環(huán)境
if (true) {
var color = 'blue'
}
alert(color) // true
b. for 語句創(chuàng)建的變量即使在for循環(huán)執(zhí)行環(huán)境結束后,也依舊會存在于循環(huán)外部的執(zhí)行環(huán)境中
for (var i = 0; i < 10; i++) {
alert(i)
}
alert(i) // 10
4.2.3 局部變量只在函數(shù)執(zhí)行時存在
4.2.4 垃圾收集
- 標記清除 - JavaScript中最常用的垃圾收集方式
- 解除引用 - 一旦數(shù)據(jù)不再有用,可通過將其值設置為null來釋放其引用
5. 引用類型
構造函數(shù): 創(chuàng)建新對象定義
5.1 Object類型
5.1.1 創(chuàng)建實例
- new
var person = new Object ()
person.name = 'hahah'
- 字面量方式
var person = {
name: 'hahah'
}
5.1.2 讀屬性
alert (person.name)
alert (person['name']) // 可為變量
5.2 Array類型
5.2.1 lenth屬性
- 數(shù)組的length屬性設置,可以從數(shù)組的末尾移除項或向數(shù)組中添加新項
var colors = ['blue','red']
colors.length = 1
alert(colors[1]) // undefined
5.2.2 檢驗數(shù)組
- Array.isArray()
5.2.3 屬性

var person1 = {
toLocaleString: function () {
return 'John'
},
toString: function () {
return 'Lily'
}
}
alert(person1) // Lily
alert(person1.toString) // Lily
alert(person1.toLocaleString) // John
5.2.4 棧方法 (后進后出)
- push() 接受參數(shù),設置尾部,并修改長度
- pop() 從數(shù)組末尾移除最后一項,并返回
var colors = ['red', 'blue']
colors.push('black')
alert(colors.length) // 3
var item = colors.pop()
alert(item) // 'black'
5.2.5 隊列方法 (后進先出)
- shift() 移除數(shù)組中的第一項并返回該項
- unshift() 添加任意值,并返回長度
var colors = ['red', 'blue']
var item = colors.shift()
alert(colors.length) // 1
alert(item) // 'red'
5.2.6 重排序
- reverse() 反轉
- sort() 升序排列
先調用toString(), 比較排序
5.2.7 操作方法
- concat() 復制當前數(shù)組,有參數(shù)的話,將參數(shù)添加到結果數(shù)組
var size = [1, 2, 3]
var newSize = size.concat([4, 5])
alert(newSize) // 1, 2, 3, 4, 5
- slice() 截取
var size = [1, 2, 3]
var size1 = size.slice(1) // [2, 3] 一個參數(shù),從參數(shù)位置到末尾均返回
var size2 = size.slice(1, 2) // [2] 兩個參數(shù),從參數(shù)1位置到參數(shù)2位置之間的數(shù),但不包含位置2
- splice()
刪除 - 2個參數(shù),第一個參數(shù)為第一項的位置,第2個參數(shù)代表要刪除的項數(shù)
插入 - 3個參數(shù),起始位置,要刪除的項數(shù),需要插入的項
替換
var size = [1, 2, 3]
var size1 = size.splice(0, 1) // 1
var size2 = size.splice(1, 0 ,4, 5) // 2, 4, 5, 3
5.2.8 位置方式
- iindexOf() 頭查
- lastIndexOf() 尾查
var numbers = [1,2,3,4,5,6]
alert(numbers.indexOf(4)) // 5
5.2.9 迭代方式
- every 所有的為true, 則為true
var nums = [1,3,4,5]
var everyRes = nums.every(function (item, index, array) {
return (item > 2)
})
everyRes() // false
- filter 過濾符合條件的數(shù)據(jù)
var nums = [1, 2, 3, 4]
var everyRes = nums.filter(function (item, index, array) {
return (item > 2)
})
everyRes() // [3, 4]
- forEach 遍歷執(zhí)行
- map 遍歷數(shù)據(jù)
var nums = [1, 2, 3, 4]
var everyRes = nums.map(function (item, index, array) {
return (item * 2)
})
everyRes() // [2, 4, 6, 8]
5.2.10 歸并方式
- reduce 小到大
- reduceRight 大到小
var nums = [1, 2, 3, 4]
var everyRes = nums.reduce(function (prev, cur, index,array) {
return (prev + cur)
})
everyRes() // 10
5.3 Date類型
var start = new Date() // 時間戳
var start1 = Date.now() // 方法前
...function
var stop1 = Date.now() // 方法后
var res = stop1 - start1 // 執(zhí)行時間
5.4 RegExp類型 - 正則表達式
5.5 Function類型
5.5.1 函數(shù)聲明
- 函數(shù)聲明提升
alert(num(10, 10)) // 20
funtion sum (sum1, sum2) {
return sum1 + sum2
}
- 函數(shù)表達式必須等到解析器執(zhí)行到它所在的代碼行
alert(num(10, 10)) // 報錯
var sum = sum (sum1, sum2) {
return sum1 + sum2
}
5.5.2 內部屬性(arguments、this)
- callee屬性 - 是一個指針 - 指向擁有arguments對象
function test (num) {
if (num <= 1) {
return 1
} else {
return num * test(num - 1) 等價于 return num * arguments.callee(num - 1)
}
}
好處: 運用callee與函數(shù)名無關,均可以調用
var realTest = test()
function test (num) {
return 0
}
realTest (5) // 120
test (0)
- this 引用的是函數(shù)執(zhí)行的環(huán)境對象(
當全局調用時,是windows對象)
window.color = 'red'
var obj = {
color: 'blue'
}
func0tion sayColor () {
alert(this.color)
}
sayColor() // red
obj.sayColor = sayColor
obj.sayColor() // blue
- caller 屬性保存著調用當前函數(shù)的函數(shù)的引用
func0tion outer() {
inner()
}
func0tion inner() {
alert(inner.caller) // outer()
}
outer()
inner.caller == auguments.callee.caller
5.5.3 屬性和方法
- prototype 不可枚舉,無法for in 發(fā)現(xiàn)
- apply / call 設置函數(shù)體內this對象的值, apply(在其中運行函數(shù)的作用域, 參數(shù)數(shù)組),call(在其中運行函數(shù)的作用域, {...傳遞參數(shù) 或者arguments})
用武之地: 可以擴充函數(shù)賴以運行的作用域
window.color = 'red'
var obj = {
color: 'blue'
}
func0tion sayColor () {
alert(this.color)
}
sayColor() // red
sayColor.call(this) // red
sayColor().call(window) // red
sayColor().call(obj) // blue
- bind() 這個方法會創(chuàng)建一個函數(shù)的實例,其this值會被綁定到傳給bind()函數(shù)
window.color = 'red'
var obj = {
color: 'blue'
}
func0tion sayColor () {
alert(this.color)
}
sayColor() // red
sayColor.bind(obj) // blue
5.6 基本包裝類型 Boolean , Number , String

5.6.1 使用new調用基本包裝類型的構造函數(shù)
var value = '25'
var obj = new Number(value)
alert(typeof obj) // object
alert(obj.instanceOf (String)) // true
5.6.2 String類型

5.7 單體內置對象
不依賴宿主環(huán)境的對象,程序執(zhí)行前就已存在
5.7.1 global
不屬于任何其他對象的屬性和函數(shù),都是全局global對象所有
var global = function () {
return this
} // 全局對象
5.7.2 Math對象

6. 面向對象的程序設計
6.1 理解對象
6.1.1 屬性類型 - 為了實現(xiàn)JavaScript引擎用的,不能直接訪問
-
數(shù)據(jù)屬性 - 包含數(shù)據(jù)值
image.png
Object,defineProperty()可修改屬性的特性
var person = {}
Object.defineProperty(person, "name", {
writable: false, // 不可修改
value: 'hahah'
})
person.name = 'lalala'
person.name // hahah'
一旦改變特性,無法更改
Object.defineProperty(person, "name", {
writable: true, // 報錯
})
-
訪問器屬性 - 不包含數(shù)據(jù)值,有getter和setter
image.png
只有getter不能寫入,只有setter無法讀取
var book = {
_year: 2021, // _表示只能通過對象方法訪問的屬性
edition: 1
}
Object.defineProperty(book, "year", {
get: function () {
return this.year
},
set: function (value) {
if (value > 2021) {
this.year = value
editiion += value - 2021
}
}
})
book.year = 2022
alert(book.edition) // 2
6.1.2 定義多個屬性 - Object.defineProperties()
var book = {}
Object.defineProperty(book, {
_year: {
writable: false, // 不可修改
value: 'hahah'
},
year: {
get: function () {
return this.year
},
set: function (value) {
if (value > 2021) {
this.year = value
editiion += value - 2021
}
}
}
})
book.year = 2022
alert(book.edition) // 2
6.1.3 讀取屬性的特性 - Object.getOwnPropertyDescriptor()
var descriptor = Object.getOwnPropertyDescriptor(book, 'year')
alert(descriptor.value) // hahah
6.2 創(chuàng)建對象
6.2.1 工廠模式
為了解決使用同一個接口創(chuàng)建許多對象,會產生大量的代碼,抽象創(chuàng)建具體對象的過程如下
function createPerson (name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function () {
alert(this.name)
}
return o
}
ver person1 = createPerson ('haha', 17, 'nurse')
ver person2 = createPerson ('hello', 17, 'doctor')
無法解決對象識別問題,引出構造函數(shù)模式
6.2.2 構造函數(shù)模式
- 構造函數(shù)本身也是函數(shù),用來創(chuàng)建對象
function createPerson (name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function () {
alert(this.name)
}
}
ver person1 = new createPerson ('haha', 17, 'nurse')
ver person2 = new createPerson ('hello', 17, 'doctor')
- 構造new操作符步驟
① 創(chuàng)建一個新對象
② 將構造函數(shù)的作用域賦值給新對象,this指向新對象
③ 執(zhí)行構造函數(shù)中的代碼(為這個對象添加屬性和方法)
④ 返回新對象
alert(person1.constructor == createPerson) // true
- 構造函數(shù)勝過工廠函數(shù)
創(chuàng)建自定義的構造函數(shù)意味著將來可以將它的實例標識為一種特定的類型 - 構造函數(shù)與其他函數(shù)的區(qū)別
調用方式不同(new) - 構造函數(shù)的問題
每個方法都要在每一個實例上重新創(chuàng)建一遍(Funtion實例) - 不同實例上的同名函數(shù)是不相等的
person1.sayName == person2.sayName // false
6.2.3 原型模式
- prototype 屬性是一個指針,指向一個對象,好處是:可以讓所有對象實例共享它所包含的屬性和方法
function person () {}
person.prototype.name = 'ha'
person.prototype.sayName = function () {
alert(this.name)
}
ver person1 = new person ()
ver person2 = new person ()
person1.sayName == person2.sayName // true
person1.sayName = person2.sayName // ha
person.prototype.isPrototypeOf(person1) // true
Object.getPrototypeOf(person1)


- 可以通過對象實例訪問保存原型的值,但是無法通過實例修改原型的值
function person () {}
person.prototype.name = 'ha'
ver person1 = new person ()
ver person2 = new person ()
person1.name = 'hello'
person2.name // ha
person1.name // hello
- 使用delete符可以完全刪除實例屬性
delete person1.name
person1.name = 'ha'
- hasOwnProperty(Object繼承) 在實例 - 相反 - hasPropertypeProperty() 在原型
判斷屬性存在原型中還是實例中
person1.name = 'hello'
person1.hasOwnProperty('name') // true
person2.hasPropertypeProperty('name') // false
- 原型與In操作符
"name" in person1 // true
"name" in person2 // true
- Object.keys 取屬性集合
var keys = Object.keys(person, Prototype)
keys['name']
- 原型語法更簡潔話(對象字面量)
person.prototype = {
name: 'hello'
}
// 以上方法,constructor指向了Object對象。而非person
var person1 = new person()
person1.instantceOf(Object) // true
person1.instantceOf(person) // true
person1.constructor(Object) // true
person1.constructor(person) // false
可以通過設置constructor改回指向
person.prototype = {
constructor: person,
name: 'hello'
}
但更改后,無法通過for in 讀取,[[Enumerable]] = true
- 動態(tài)性
重寫原型對象切斷了現(xiàn)有原型與任何之前已經存在的對象實例之間的聯(lián)系,他們引用的仍是最初的原型
function person () {}
var person1 = new person()
person.prototype = { // 新的person.newProperty
constructor: person,
name: 'hello'
}
person1.name // error 讀取的是person的property
- 原生對象的原型
① 通過原生對象的原型, 不僅可以取得所有默認方法的調用,也可以定義新方法
alert(typeof Array.prototype.sort) // true
String.propotype.startWith = function (text) {
return this.indexOf(text) == 0
}
var msg = 'Hello'
alert(msg.startWith('Hello')) // true
② 構造函數(shù)可以與原型模式組合
③ 構造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性
6.2.4 穩(wěn)妥的構造函數(shù)
指的是沒有公共屬性,而且其方法也不引用this對象
function createPerson (name, age, job) {
var o = new Object()
o.sayName = function () {
alert(name)
}
}
ver person1 = new createPerson ('haha', 17, 'nurse')
person1.sayName // haha , 唯一輸出name的調用
6.3 繼承

6.3.1 原型鏈
基本思想:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法
function SuperType () {
this.property = true
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
function SubType () {
this.subProperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
return this.subProperty
}
var instance = new SubType()
alert(instance.getSuperValue()) // true
SubType繼承了SuperType,原來存在于SuperType的實例中的所有屬性和方法,現(xiàn)在也存在于SubType的prototype中
- instance.constructor 已經變成了SuperType
- 所有的函數(shù)默認都是Object實例
instance -> SubType -> SuperType -> Object - 通過原型鏈繼承時,不能使用字面量創(chuàng)建原型方法,否則會重寫原型鏈
- 原型鏈繼承有個問題:通過原型鏈繼承時,原先的實例屬性會變成現(xiàn)在的原型屬性(引用類型值)
6.3.2 借用構造函數(shù)
基本思想: 在子類型構造函數(shù)的內部調用超類型構造函數(shù)
function SuperType () {
this.colors = ['red','blue','black']
}
function SubType () {
SuperType.call(this)
}
var instantce1 = new SubType()
instantce1.colors.push('green')
var instantce2 = new SubType()
instantce2.colors // ['red','blue','black']
instantce1.colors // ['red','blue','black','green']
- 可傳遞參數(shù)
function SuperType (color) {
this.colors = color
}
function SubType () {
SuperType.call(this, 'black')
this.age = 17
}
var instantce1 = new SubType()
instantce1.colors // black
instantce1.age // 17
- 問題:方法都在構造函數(shù)中,無法復用
6.3.3 組合繼承(原型鏈+借用構造函數(shù))
function SuperType (name) {
this.name = name
this.colors = ['blue', 'red']
}
SuperType.prototype.sayName = function () {
return this.name
}
function SubType () {
this.subProperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
return this.subProperty
}
var instance = new SubType()
alert(instance.getSuperValue()) // true
6.3.4 原型式繼承:一個對象作為另一個對象的基礎
function object (o) {
function F () {}
F.prototype = o
return new F()
}
var person = {
friends: ["Lily']
}
var person1 = object(person)
var person2 = object(person)
person1.friends.push('Bob')
person2.friends.push('Amy')
alert(person.friends) // ["Lily","Bob","Amy"]
6.3.5 寄生式函數(shù)
基本思想:創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內部以某種方式來增強對象,最后再像是真地是它做了所有工作一樣返回對象
function createAnother(original){
var clone = object(original) // 通過調用函數(shù)來創(chuàng)建一個新對象
clone.sayHi = function () { // 強化對象
alert('hi')
}
return clone // 返回對象
}
var person = {
name: 'hi',
friends: ['lily', 'lucy']
}
var anotherPerson = new createAnother(person )
anotherPerson.sayHi() // hi
- 使用寄生式函數(shù)繼承時,會由于不能做到函數(shù)復用而降低效率,這一點與構造函數(shù)模式類似
6.3.6 寄生組合式繼承,js最常用的繼承模式
基本思想:通過借用構造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法
function inheritPrototype (SubType, SuperType) {
var prototype = object(SuperType.prototype) // 創(chuàng)建對象
prototype.constructor = SubType // 增加對象
SubType.constructor = prototype // 指定對象
}
function SuperType (name) {
this.name = name
this.colors = ['blue', 'red']
}
SuperType.prototype.sayName = function () {
return this.name
}
function SubType (name, age) {
SuperType.call(this, name) // 第二次調用SuperType
this.age = age
}
inheritPrototype(SubType,SuperType)
SubType.prototype.sayAge = function () {
alert(this.age)
}
7. 函數(shù)表達式
function關鍵字后面沒有跟標識符,稱為匿名函數(shù)
7.1 遞歸
一個函數(shù)通過名字調用自身
function test (num) {
if (num <= 1) {
return 1
} else {
return num * test(num - 1) 等價于 return num * arguments.callee(num - 1)
}
}
arguments.callee嚴格模式無法訪問,可以使用命名函數(shù)來達成相同效果
7.2 閉包
- 指有權訪問另一個函數(shù)作用域中的變量的函數(shù)
7.2.1 閉包與變量
閉包所保存的是整個變量對象,而不是某個特殊的變量
function createFunction () {
var result = new Array()
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i
}
}
return result
}
實際上,每個函數(shù)都返回10,因為每個函數(shù)的作用域鏈都保存著
function createFunction函數(shù)的活動對象,所以他們引用的是一個變量,當函數(shù)返回后,i的變量均為10,可通過創(chuàng)建另一個匿名函數(shù)強制讓閉包的行為符合預期。
function createFunction () {
var result = new Array()
for (var i = 0; i < 10; i++) {
result[i] = function (num) {
return function () {
return num
}
}(i)
}
return result
}
函數(shù)是按值傳參,i賦值給num,匿名函數(shù)會存在num的副本,因為會返回不同i值

