原型對象 `prototype`
- 原型對象的全部屬性和方法,都能被實例對象共享;
JavaScript 經過構造函數生成新對象,所以構造函數能夠視爲對象的模板。實例對象的屬性和方法,能夠定義在構造函數內部。
function Cat (name, color) {
this.name = name;
this.color = color;
}
var cat1 = new Cat('小明', '白色');
cat1.name // '小明'
cat1.color // '白色'
構造函數缺點:
- 一個構造函數的多個實例之間,沒法共享屬性,從而形成對系統資源的浪費。
解決方法,就是 JavaScript 的
原型對象`prototype`,由於原型對象的全部屬性和方法,都能被實例對象共享。
function Cat (name, color) {
this.name = name;
}
Cat.prototype.color ="白色";
}
var cat1 = new Cat('巧克力');
var cat2 = new Cat('布丁');
cat1.color //白色
cat2.color//白色
JavaScript規定,每一個函數都有一個`prototype`屬性,指向一個對象。
- 對於構造函數來講,生成實例的時候,該屬性會自動成爲實例對象的原型。
- 實例對象自身就有某個屬性或方法,它就不會再去原型對象尋找這個屬性或方法。
function f() {}
typeof f.prototype // "object"
每一個 JS 對象都有 `
__proto__` 屬性,這個屬性能夠訪問到 原型(`
[[prototype]]`) 內部屬性。這個屬性在如今來講已經不推薦直接去使用它了。
原型鏈
- 任何對象,均可以充當其餘對象的原型;原型對象也有本身的原型。
- 對象到原型,再到原型的原型,一層層向上找到`Object.prototype` 造成原型鏈。
能夠說
基本上全部對象都繼承了`Object.prototype`的屬性;而 `Object.prototype` 的原型是 `null`
經過 `
getPrototypeOf` 方法返回參數對象原型
Object.getPrototypeOf(Object.prototype) //null
讀取對象屬性時,引擎會先在自身屬性上查找,沒有就查找原型,一級級向上查找,若是到`Object.prototype`仍是沒有,則返回`undefined`。
**一級級向上,對性能有影響,尋找的層級越多,性能影響越大**
**原型鏈:**
- 實例 F1 經過`__proto__` 訪問對應構造函數的原型 -> `FOO.prototype`
- 函數原型再經過`__proto__` 訪問Object的原型 -> `Object.prototype`
- Object的原型的`__proto__` 指向 `null`
- 構造函數 `Foo.prototype` 經過`constructor`訪問構造函數自己
- `Object.constructo` 指向 `Function`
- `Function.constructo` 指向 自己
- `Function.__proto__` 指向一個匿名函數
- 每一個構造函數經過 `prototype` 訪問原型
**只有函數才擁有`prototype`屬性,基本上全部函數都有這個屬性**
let fun = Function.prototype.bind()
當聲明一個函數時自動建立 `prototype` 屬性,
這個屬性的值是一個對象(也就是原型),且只有一個屬性 `constructor`
`constructor`
`prototype` 有一個屬性 `constructor`,默認指向原型所在的構造函數
function Fn (){}
var f = new Fn ();
f.constructor == Fn //true
f.constructor == Function //false
//能夠從實例對象新建另外一個實例
var b =new f.constructor();
b.constructor == Fn //true
`constructor `是一個公有且不可枚舉的屬性。一旦咱們改變了函數的 prototype ,那麼新對象就沒有這個屬性了,若是修改了原型對象,通常會同時修改constructor屬性,防止引用的時候出錯。
function A(){}
console.log(A.prototype) // {constructor: ƒ}constructor: ƒ A()__proto__: Object
A.prototype="a"
console.log(A.prototype) //"a"
constructor做用:
- 讓實例對象知道是什麼函數構造了它,能夠得知某個實例對象,是哪個構造函數產生的。
- 若是想給某些類庫中的構造函數增長一些自定義的方法,就能夠經過 xx.constructor.method 來擴展
\_\_proto\_\_
基本上每一個對象都有的隱式原型屬性,指向建立該對象的構造函數的原型,實際指向`[[prototype]]`, 內部屬性,咱們並不能訪問到,因此使用 _proto_ 來訪問。
console.log({})
//__proto__: Objectconstructor: ƒ Object()hasOwnProperty: ....
當咱們使用 new 操做符時,生成的實例對象擁有了 `__proto__`屬性。
function Foo() {}
// 這個函數是 Function 的實例對象
// function 就是一個語法糖
// 內部調用了 new Function(...)
const a =1;
const fn = new Function("console.log(a)") // Function 的this始終指向 全局對象,除非手動改變this指向
fn()//1
-
全部對象均可以經過原型鏈最終找到 Object.prototype ,雖然 Object.prototype 也是一個對象,可是這個對象卻不是 Object 創造的,而是引擎本身建立了 `Object.prototype` 。
能夠這樣說,全部實例都是對象,可是對象不必定都是實例。
-首先引擎建立了 Object.prototype ,而後建立了 Function.prototype ,而且經過__proto__ 將二者聯繫了起來。
- Function.prototype 之後纔有了 function Function() ,而後其餘的構造函數都是 function Function() 生成的。
-
函數經過 `new Function()` 生成, 不是全部函數都是 `new Function() `產生的。
總結:
> 1. Object 是全部對象的爸爸,全部對象均可以經過__proto__ 找到它
> 2. Function 是全部函數的爸爸,全部函數均可以經過__proto__ 找到它
> 3. Function.prototype 和 Object.prototype 是兩個特殊的對象,他們由引擎來建立
> 4. 除了以上兩個特殊對象,其餘對象都是經過構造器 new 出來的
> 5. 函數的 prototype 是一個對象,也就是原型
對象的__proto__ 指向原型,__proto__將對象和原型鏈接起來組成了原型鏈
JavaScript 對象繼承 OOP (三)
html