前端筆記 — JS面向?qū)ο?/h1>

一. 創(chuàng)建對(duì)象

創(chuàng)建對(duì)象的四個(gè)步驟

  • 創(chuàng)建一個(gè)新的對(duì)象
  • 綁定作用域(將構(gòu)造函數(shù)的作用域綁定到對(duì)象上)
  • 初始化對(duì)象(執(zhí)行構(gòu)造函數(shù)的代碼)
  • 返回新對(duì)象
1. 工廠(chǎng)模式
function createPerson(name) {
    let obj = new Object()
    obj.name = name
    obj.sayName = function () {
        console.log(this.name)
    }
    reutrn obj
}
let p = createPerson('張三')
2. 構(gòu)造函數(shù)模式
function Person (name, age) {
     this.name = name
     this.sayName= function () {
          console.log(this.name)
     }
}
let p = new Person('張三')
3. 原型模式
function Person () {
}
Person.prototype.name = '張三'
Person.prototype.sayName = function () {
    console.log(this.name)
}
4. 組合模式

構(gòu)造函數(shù)用于定義實(shí)例屬性惕橙,原型用于定義方法和共享屬性

function Person (name) {
    this.name = name
}
Person.prototype = {
    constructor: Person,
    sayName () {
        console.log(this.name)
    }
}
5. 動(dòng)態(tài)原型模式

把所有信息都封裝在構(gòu)造函數(shù)中,在構(gòu)造函數(shù)中初始化原型

function Person (name) {
    this.name = name
    if (typeof this.sayName !== 'function') {
        Person.prototype.sayName = function () {
            console.log(this.name)
        }
    }
}
6. 寄生構(gòu)造函數(shù)模式

創(chuàng)建一個(gè)函數(shù),該函數(shù)封裝創(chuàng)建對(duì)象的代碼锐墙,然后返回新創(chuàng)建的對(duì)象佩番,除了使用new操作符外吗冤,這個(gè)模式跟工廠(chǎng)模式一模一樣

function Person(name) {
    let obj = new Object()
    obj.name = name
    obj.sayName = function () {
        console.log(this.name)
    }
    return obj
}
let p = new Person('張三')
7. 穩(wěn)妥構(gòu)造函數(shù)模式

沒(méi)有公共屬性缓艳,其方法也不引用this對(duì)象槽奕,不使用new操作符調(diào)用構(gòu)造函數(shù)

function Person (name) {
    let obj = new Object()
    obj.sayName = function () {
        console.log(name)
    }
    return obj
}
let p = Person('張三')

二. 數(shù)據(jù)屬性與訪(fǎng)問(wèn)器屬性

1.數(shù)據(jù)屬性

數(shù)據(jù)屬性有4個(gè)特性嘴纺,configurable败晴,表示能否通過(guò)delete刪除,能否修改其特性栽渴。enumerable尖坤,表示能否通過(guò)for-in遍歷。writable闲擦,表示能否修改屬性的值慢味。value场梆,表示屬性的值

直接定義在對(duì)象上的屬性為數(shù)據(jù)屬性,它們的configurable纯路、enumerable及writable都為true或油,通過(guò)Object.defineProperty添加的數(shù)據(jù)屬性,以上三個(gè)值都為false

2.訪(fǎng)問(wèn)器屬性

訪(fǎng)問(wèn)器屬性有4個(gè)特性感昼,前兩個(gè)與數(shù)據(jù)屬性相同装哆。get,讀取該屬性時(shí)調(diào)用的函數(shù)定嗓,默認(rèn)為undefined蜕琴。set,寫(xiě)入該屬性時(shí)調(diào)用的函數(shù)宵溅,默認(rèn)為undefined

屬性的set/get方法通過(guò)Object.defineProperty()方法來(lái)設(shè)置
該方法接收三個(gè)參數(shù)凌简,第一個(gè)參數(shù)為需要設(shè)置的對(duì)象,第二個(gè)參數(shù)為對(duì)象的屬性恃逻,第三個(gè)參數(shù)為設(shè)置的內(nèi)容

function Product () {
     Object.defineProperty(this, 'price', {
          get: function () {return price * 0.9},
          set: function (value) {
               if (value <= 0) {
                    console.log('金額必須大于0')
               } else {
                    price = value
               }
          }
     })
}
3.getOwnPropertyDescriptor

該方法可以獲取給定屬性的描述雏搂,返回的是一個(gè)對(duì)象,如果是訪(fǎng)問(wèn)器屬性寇损,這個(gè)對(duì)象的屬性有configurable凸郑、enumerable、get和set矛市。如果是數(shù)據(jù)屬性芙沥,這個(gè)對(duì)象的屬性有configurable、enumerable浊吏、writable及value

三. 公有/私有屬性

公有屬性:使用對(duì)象的人可以訪(fǎng)問(wèn)的對(duì)象屬性
私有屬性:使用對(duì)象的人無(wú)法訪(fǎng)問(wèn)的對(duì)象屬性

function Person () {
     this.name = name // 公有屬性
     var age = 10 // 私有屬性
     //公有方法
     this.showName = function () {
          console.log(‘我叫' + this.name)
     }
     //私有方法
     var that = this
     var showInfo = function () {
          console.log('我是' + that.name) // 私有方法中使用this無(wú)法獲取到當(dāng)前對(duì)象而昨,所以用that替代
     }
}

四. 類(lèi)型判斷

有如下變量

var a = 1
var b = 'abc'
var c = []
var d = {}
1.typeof

typeof弊端:無(wú)論引用的是什么類(lèi)型的對(duì)象,都返回object

typeof a // number
typeof b // string
typeof c // object
typeof d // object
//new Number(1)找田、new String('abc') 與 new Boolean(false) 返回的也是 object
2.toString.call()

通過(guò)調(diào)用toString方法來(lái)判斷類(lèi)型

toString.call(a) // [object Number]歌憨,與 new Number(1)結(jié)果一樣 
toString.call(b) // [object String],與 new String('ab')結(jié)果一樣
toString.call(c) // [object Array]
toString.call(d) // [object Object]
3.instanceof

instanceof 后面只能跟對(duì)象類(lèi)型

a instanceof Number // false墩衙,new Number(1) 為 true
b instanceof String // false务嫡,new String('abc') 為 true
c instanceof Array  // true
d instanceof Object // ture
4.constructor

通過(guò)該屬性可以判斷是通過(guò)那個(gè)構(gòu)造函數(shù)創(chuàng)建的

a.constructor === Number // new Number(1)一樣
b.constructor === String // new String('abc')一樣
c.constructor === Array
d.constructor === Object

五. 原型

1.雙對(duì)象法則

其實(shí)所謂的對(duì)象包含兩個(gè)獨(dú)立的對(duì)象:構(gòu)造函數(shù)對(duì)象,原型對(duì)象(prototype)
原型對(duì)象只分配一次內(nèi)存
實(shí)例化的過(guò)程其實(shí)就是拷貝構(gòu)造函數(shù)屬性的過(guò)程
實(shí)例的某些屬性漆改,值不一樣需要單獨(dú)的空間植袍,但是某些方法可能完全一樣,如果每個(gè)實(shí)例都有一份則會(huì)浪費(fèi)內(nèi)存空間籽懦,所以使用原型來(lái)定義方法,所有實(shí)例對(duì)象共享一份氛魁。
當(dāng)我們new 一個(gè)實(shí)例后暮顺,系統(tǒng)自動(dòng)做了如下事情

  1. 創(chuàng)建一個(gè)空對(duì)象 var p = {}
  2. 拷貝構(gòu)造函數(shù)中的方法屬性到空對(duì)象中
  3. 自動(dòng)生成一個(gè)__proto__屬性指向原型對(duì)象
    p.__proto__ = xxx.prototype
2.屬性搜索機(jī)制

訪(fǎng)問(wèn)對(duì)象的某個(gè)屬性時(shí)厅篓,會(huì)先在對(duì)象自身的屬性列表中尋找,如果找不到捶码,則通過(guò)隱藏的proto屬性(該屬性保存原型對(duì)象的內(nèi)存地址)找到原型對(duì)象羽氮,然后從原型對(duì)象的屬性列表中尋找,直到找到該屬性或proto為null為止

var fun = function () {}
fun.prototype = {
     info: {
          name: 'peter',
          age: 25
     },
     height: 170
}
Object.defineProperty(fun.prototype, 'weight', {
     get () { return weight },
     set (value) { weight = value }
})
var a = new fun()
var b = new fun()
a.info.name = 'jack'
b.info.name = 'tom'
a.height = 175
b.height = 180
a.weight = 60
b.weight = 70

//info為原型對(duì)象屬性惫恼,所有實(shí)例共享一份档押,所以修改b的值后,a的值也會(huì)發(fā)生變化
a.info.name // tom
b.info.name // tom

//height雖然也是原型對(duì)象屬性祈纯,但是為值類(lèi)型令宿,所以不會(huì)共享,每個(gè)實(shí)例都有一份
a.height // 175
b.height // 180

//weight雖然是值類(lèi)型的原型對(duì)象屬性腕窥,但是通過(guò)defineProperty方式添加的粒没,所以會(huì)共享
a.weight // 70 
b.weight // 70
3.屬性屏蔽理論

構(gòu)造函數(shù)中的屬性(方法)會(huì)屏蔽掉原型中的同名屬性

function Person () {
     this.name = '張三'
     this.show = function () {
          console.log('我是構(gòu)造函數(shù)的方法')
     }
}
Person.prototype = {
     name: '李四',
     show: function () {
          console.log('我是原型的方法')
     }
}
var p =  new Person()
console.log(p.name) //張三
p.show() // 我是構(gòu)造函數(shù)的方法

如果需要訪(fǎng)問(wèn)原型中的同名屬性,可以通過(guò)delete清除構(gòu)造函數(shù)中的屬性

delete p.name
delete p.show
console.log(p.name) // 李四
p.show()  // 我是原型的方法

也可以直接通過(guò) 類(lèi)名.prototype.屬性名 來(lái)訪(fǎng)問(wèn)

console.log(Person.prototype.name) // 李四
Person.prototype.show() // 我是原型的方法

六. call簇爆、apply及bind方法

1.call方法

會(huì)將借用對(duì)象方法中的this改為自身

var Person = function () {
     this.show = function () {
          console.log('這是Person的方法癞松,年齡' + this.age)
     }
}
var Student = function () {
     this.show = function () {
          console.log('這是Student的方法,年齡' + this.age)
     }
}
var s = new Student()
s.age = 20
s.show() // 這是Student的方法入蛆,年齡20
var p = new Person()
p.show.call(s) // 這是Person的方法响蓉,年齡20

借用方法時(shí)也可以傳遞參數(shù)

var add = function (num1, num2) {
      console.log(num1 + num2)
}
var show = function () {
     console.log('哈哈哈哈哈')
}

add.call(show, 1, 2) // 3
2.apply方法

apply與call的用法幾乎一樣
call的傳參是離散的,而apply是把所有參數(shù)放在一個(gè)數(shù)組傳遞
apply雖然傳遞的是數(shù)組哨毁,但是使用時(shí)是把數(shù)組拆開(kāi)的

var arr1 = [1, 2, 3, 4]
Array.prototype.push.apply(arr1, [5, 6, 7, 8])
====>
Array.prototype.push.call(arr1, 5, 6, 7, 8)
====>
arr1.push(5, 6, 7, 8)
3.bind方法

bind方法與apply和call很相似枫甲,也是可以改變函數(shù)體內(nèi)this的指向
bind方法會(huì)創(chuàng)建一個(gè)新函數(shù),稱(chēng)為綁定函數(shù)挑庶,當(dāng)調(diào)用這個(gè)綁定函數(shù)時(shí)言秸,綁定函數(shù)會(huì)以第一個(gè)參數(shù)作為this,第二個(gè)及后面的參數(shù)加上本身的參數(shù)作為原函數(shù)的參數(shù)來(lái)調(diào)用原函數(shù)

var bar = function () {
     console.log(this.x)
}
var foo = { x: 3 }
bar() // undefined
bar.bind(foo)() // 3

七. callee/caller屬性

1.callee屬性

callee 是 arguments 的一個(gè)屬性成員迎捺,它表示對(duì)函數(shù)對(duì)象本身的引用

function test(a, b) {
     console.log(arguments.length) // 3举畸,實(shí)參的個(gè)數(shù)
     console.log(test.length) // 2,形參的個(gè)數(shù)
     console.log(arguments.callee) // arguments.callee === test
}
test(1, 2, 3)

callee可以用來(lái)遞歸函數(shù)

function sum(n) {
     if (n > 0) return n + arguments.callee(n - 1)
     // 等價(jià)于 if (n > 0) return n + sum(n - 1)
     return 0
}
console.log(sum(5)) // 15
2.偽數(shù)組

偽數(shù)組就是一個(gè)包含length屬性的json對(duì)象凳枝,key都是0, 1, 2, 3......
偽數(shù)組每次都要自己計(jì)算length個(gè)數(shù)抄沮,arguments就是一個(gè)偽數(shù)組

我們可以通過(guò)下面的方式將偽數(shù)組轉(zhuǎn)換成數(shù)組

var arr1 = {0: 'a', 1: 'b', length: 2}
Array.prototype.slice.call(arr1) // ["a", "b"]
var arr2 = {length: 2}
Array.prototype.slice.call(arr2) // [undefined, undefined]
3.caller屬性

caller屬性表示當(dāng)前函數(shù)被哪個(gè)函數(shù)調(diào)用

function show () {
     console.log(show.caller.toString())
}
function test () {
     show()
}
test() // f test () { show() }
show() // null 

八. 原型鏈

所謂原型,就是prototype岖瑰、proto 和 constructor 的三角關(guān)系

function Foo() {}
var f1 = new Foo
1.prototype

構(gòu)造函數(shù)有一個(gè)prototype屬性叛买,指向它的原型對(duì)象
實(shí)例對(duì)象沒(méi)有prototype屬性

2.constructor

構(gòu)造函數(shù):用來(lái)初始化新創(chuàng)建的對(duì)象的函數(shù),在例子中蹋订,F(xiàn)oo就是構(gòu)造函數(shù)
通過(guò)同一個(gè)構(gòu)造函數(shù)實(shí)例化的多個(gè)對(duì)象具有相同的原型對(duì)象

原型對(duì)象有一個(gè)constructor屬性率挣,指向該原型對(duì)象對(duì)應(yīng)的構(gòu)造函數(shù)

Foo.prototype.constructor === Foo

重新定義原型對(duì)象后,其constructor屬性會(huì)指向Object()

Foo.prototype = {} //  Foo.prototype.constructor === Object

如果重新定義原型對(duì)象不希望改變其constructor露戒,可設(shè)置為之前的constructor

Foo.prototype = {
    constructor: Foo
    ... // 其他屬性
}

實(shí)例本身沒(méi)有constructor屬性椒功,但由于實(shí)例可以繼承原型對(duì)象的屬性捶箱,所以也擁有了constructor屬性,同樣指向構(gòu)造函數(shù)

f1.constructor === Foo.prototype.constructor === Foo
function Foo () {}
====> 等價(jià)于
var Foo = new Function()
//所以可以看做Foo是Function的實(shí)例對(duì)象动漾,F(xiàn)oo本身沒(méi)有constructor丁屎,而是繼承自Function.prototype,所以
Foo.constructor === Function.prototype.constructor === Function
3.proto

實(shí)例對(duì)象有一個(gè)proto屬性旱眯,指向構(gòu)造函數(shù)的原型對(duì)象

f1.__proto__ === Foo.prototype // Foo是創(chuàng)建f1的構(gòu)造函數(shù)

任何函數(shù)都可以看成是Function的實(shí)例晨川,F(xiàn)unction也可以看成是調(diào)用自身的new操作實(shí)例化的,所以

Foo.__proto__ === Function.prototype
Function.__proto__ === Function.prototype

Foo.prototype作為原型對(duì)象删豺,同時(shí)也是實(shí)例對(duì)象共虑。任何對(duì)象都可以看做是通過(guò)Object()構(gòu)造函數(shù)實(shí)例化的對(duì)象

Foo.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype

Object()也是函數(shù),它的構(gòu)造函數(shù)就是Function

Object.__proto__ === Function.prototype

九. 繼承

1.原型鏈繼承
function Super () {
    this.name = '張三'
}
function Sub (age) {
    this.age = age
}
Sub.prototype = new Super()
let sub = new Sub(20)
console.log(sub.name) // 張三
2.構(gòu)造函數(shù)繼承
function Super (name) {
    this.name = name
}
function Sub (name, age) {
    Super.call(this, name)
    this.age = age
}
let sub = new Sub('張三', 20)
3.組合繼承
function Super (name) {
    this.name = name
}
Super.prototype.job = 'teacher'
function Sub (name) {
    Super.call(this, name)
}
Sub.prototype = new Super()
let sub = new Sub('張三')
console.log(sub.job) // 'teacher'
4.原型式繼承
function object (obj) {
    function F() {}
    F.prototype = obj
    reutrn new F()
}
let person = {
    name: '張三'
    age: 20
}
let p = object(person)
console.log(p.name) // 張三
5.寄生式繼承
function object (obj) {
    function F() {}
    F.prototype = obj
    return new F()
}
function createAnother (obj) {
    let another = object(obj)
    another.sayName = function () {
        console.log(this.name)
    }
    return another
}
let person = { name: '張三' }
let p = createAnother(person)
p.sayName() // 張三
6.寄生組合式繼承
function object (obj) {
    function F() {}
    F.prototype = obj
    return new F()
}
function inheritPrototype(sub, super) {
    let prototype = object(super.prototype)
    prototype.constructor = sub
    sub.prototype = prototype
}
function Super(name) {
    this.name = name
}
Super.prototype.sayName = function () {
    console.log(this.name)
}
function Sub(name, age) {
    Super.call(this, name)
    this.age = age
}
inheritPrototype(Sub, Super)
let sub = new Sub('張三', 20)
sub.sayName() // 張三
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末吼鳞,一起剝皮案震驚了整個(gè)濱河市看蚜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赔桌,老刑警劉巖供炎,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疾党,居然都是意外死亡音诫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)雪位,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)竭钝,“玉大人,你說(shuō)我怎么就攤上這事雹洗∠愎蓿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵时肿,是天一觀的道長(zhǎng)庇茫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)螃成,這世上最難降的妖魔是什么旦签? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮寸宏,結(jié)果婚禮上宁炫,老公的妹妹穿的比我還像新娘。我一直安慰自己氮凝,他們只是感情好羔巢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般朵纷。 火紅的嫁衣襯著肌膚如雪炭臭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天袍辞,我揣著相機(jī)與錄音,去河邊找鬼常摧。 笑死搅吁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的落午。 我是一名探鬼主播谎懦,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼溃斋!你這毒婦竟也來(lái)了捺氢?” 一聲冷哼從身側(cè)響起泥彤,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后茵臭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趣些,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年材蛛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片走哺。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚯嫌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丙躏,到底是詐尸還是另有隱情择示,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布晒旅,位于F島的核電站栅盲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏敢朱。R本人自食惡果不足惜剪菱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拴签。 院中可真熱鬧孝常,春花似錦、人聲如沸蚓哩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)岸梨。三九已至喜颁,卻和暖如春稠氮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背半开。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工隔披, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寂拆。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓奢米,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親纠永。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鬓长,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345