說到原型以前,先複習一下對象建立方式:app
<script> var obj = { a: 1} obj.b = 2 console.log(obj) </script>
<script> function CreatePerson (name, age) { var obj = new Object () obj.name = name obj.age = age obj.sayHi = function () { console.log('哈嘍') } return obj } var person1 = CreatePerson('張三', 18) var person2 = CreatePerson('李四', 20) </script>
<script> function Person (name, age) { this.name = name this.age = age this.sayHi = function () { console.log('哈嘍') } } var person1 = new Person('張三',18) var person2 = new Person('李四',20) </script>
用new操做符調用函數時,會自動執行下面的操做函數
- 建立(或構造)一個新的對象
- 這個新對象會執行prototype鏈接
- 這個新對象會綁定到函數調用的this
- 若是函數沒有返回其餘對象, 那麼new表達式中的函數調用自動返回這個新對象
//咱們來舉個例子 var Foo() { //這是一個普通函數 } Foo() //普通函數調用 var a = new Foo()//在普通調用前加一個new關鍵字後,這個調用就變成了構造函數調用
new會劫持全部普通函數,並用構造函數的形式調用它優化
var a = new Foo() 這句話的執行機制: Foo是一個普通函數,在使用new調用時,它就會構造一個對象,而且賦值給athis
用new的方法來調用Foo會產生一個新對象a,這個新對象的內部連接__proto__會關聯到Foo的原型對象上
a.__proto__ == Foo.prototypeprototype
<script> function Person () { } Person.prototype.name = '張三' Person.prototype.age = 18 Person.prototype.sayHi = function () { console.log('哈嘍') } var person1 = new Person() var person2 = new Person() </script>
<<script> function Person () { } Person.prototype = { name: '張三', age: 18, sayHi: unction () { console.log('哈嘍') } } var person1 = new Person() var person2 = new Person() console.log(person1.constructor == Person) // false console.log(person1.constructor == Object) // true </script>
- js 中全部的對象都有一個特殊的內置屬性[[prototype]] ,這個屬性其實就是用來對其餘對象的引用
- 這種寫方法改變了原型內部的constructor指針的屬性了 ,再也不指向Person函數了,若是這個指針很重要,那麼能夠手動設置
<<script> function Person () { } Person.prototype = { constructor:Person, name: '張三', age: 18, sayHi: unction () { console.log('哈嘍') } } var person1 = new Person() var person2 = new Person() console.log(person1.constructor == Person) // true console.log(person1.constructor == Object) //false </script>
<script> var obj = { a: 2 } console.log(obj.a) // 2 </script>
prototype機制就是存在於對象中的一個內部連接,它會引用其餘對象。指針
這個連接的做用就是: 若是在對象上沒有找到須要的屬性或者方法,引擎就會在prototype關聯的對象上進行查找。若是在後者的對象中沒有找到這個屬性和方法,引擎就會繼續查找他的prototype,以此內推,若是沒有找到,就會返回underfind。
這就是所謂的原型鏈。(有點像嵌套的做用域鏈)code
構造函數就是初始化一個實例對象,對象的prototype屬性是繼承一個實例對象。對象
<script> function Bird(legNum, wingNum) { this.leg = legNum; this.wing = wingNum; } Bird.prototype.appearance = function () { console.log('鳥類都有漂亮的羽毛') } function Swan (beh,legNum,wingNum) { this.behaviour = beh; } Swan.prototype = new Bird(2,2,);// 繼承了Bird中的屬性和方法,可是不能經過傳參修改這些值 Swan.prototype.story = function () { console.log('是由醜小鴨變成的'); } // 白天鵝 var whiteSwan = new Swan('會游泳') console.log(whiteSwan) whiteSwan.appearance() // 鳥類都有漂亮的羽毛 whiteSwan.story() // 是由醜小鴨變成的 </script>
<script> function Person (name, age) { this.name = name; this.age = age; } Person.prototype.behaviour = function () { console.log('你們好,我是一類人') } function Man(name, age, gender) { Person.call(this, name ,age); this.gender = gender; } //能夠建立多個實例對象 var man1 = new Man('張三', 18, '男') var man2 = new Man('李四', 20, '女') //可是 不能調用原型中的屬性和方法 console.log(man1) console.log(man2) man1.behaviour()//會報錯,這種方法繼承不到Person函數中原型中的屬性方法 </script>
call(this, 參數1 , 參數2...)繼承
- 優勢:能夠繼承全部在構造函數體中書寫的屬性,而且能夠進行傳參
- 缺點: 沒法繼承原型中的屬性和方法
原型方式:能夠繼承原型鏈中的屬性ip
看實例
<script> // 組合繼承 function Person (name, age) { this.name = name; this.age = age; } //Person的原型 Person.prototype.behaviour = function () { console.log('你們好,我是一類人') } //console.log(Person())//underfined //Man 的構造函數 function Man(name, age, gender) { //call調用的方法進行繼承,這個方法改變了Man這個構造函數內部的指向(即constructor指針) Person.call(this, name ,age);//第二次調用Person this.gender = gender; } //這裏的經過new建立了一個實例對象,並把這個實例對象賦值給了Man的原型對象, 因此改變了 Man原型對象的內部指針(constrouctor) //本來Man 的原型對象中的constructor指向Man這個函數的,可是如今被改變成指向Person這個函數了 Man.prototype = new Person() //第一次調用Person //Man 原型對象中的方法 Man.prototype.action = function () { console.log('吃飯、睡覺、打豆豆') } //能夠建立多個實例對象 var man1 = new Man('張三', 18, '男') var man2 = new Man('李四', 20, '女') console.log(man1) man1.behaviour() man1.action() </script>
- 優勢:全部的屬性和方法都可以被繼承,而且全部的屬性都可自由傳參。
- 缺點:每調用一次Man構造函數時,就會調用兩次Person函數
<script> //構造函數 function Person (name, age) { this.name = name; this.age = age; } Person.prototype.behaviour = function () { console.log('你們好,我是一類人') } function Man (name, age, gender) { this.gender = gender var person = new Person(name,age) // 在Man函數內部對Person進行遍歷, 把People具備的屬性賦值給Man for (var k in person) { this[k] = person[k] } } Man.prototype.action = function () { console.log('吃飯、睡覺、打豆豆') } //建立實例對象man var man = new Man('張三', 18, '男') var man2 = new Man('李四', 20, '女') console.log(man.name) console.log(man.age) console.log(man) console.log(man2) man.behaviour() </script>
優勢:能夠傳參,能夠繼承到原型中屬性和方法
<<script> //構造函數 function Person (name, age) { this.name = name this.age = age } Person.prototype.behaviour = function () { console.log('你們好,我是一類人') } function Man (name, age, gender) { //在Man中添加一個屬性people,把People這個函數體賦值給Man的屬性people,而後再進行調用 this.people = Person //進行調用 this.people(name, age) this.gender = gender } Man.prototype = function () { console.log('吃飯、睡覺、打豆豆') } //實例對象 var man = new Man('張三', 18 , '男') console.log(man) man.behaviour()// 報錯 不能繼承到Person原型中的屬性方法 </script>
- 優勢: 能夠傳參
- 缺點:原型內的方法沒法繼承,而且還多了一個屬性people