ES5
es5并沒有類class,我們只能用構(gòu)造函數(shù)來模擬類.
構(gòu)造函數(shù)
構(gòu)造函數(shù)用new操作符一起使用.new具體做了以下事情.
1.new在內(nèi)存中創(chuàng)建了一個新的空對象.
2.讓this指向了這個對象.
3.執(zhí)行構(gòu)造函數(shù)里的代碼打掘,給這個新的對象增加屬性和方法.
4.返回這個新對象揭绑,隱式return.
//構(gòu)造函數(shù)
function Animal(){
this.name='動物' //實例屬性
}
//每個創(chuàng)建出的實例身上都帶有name屬性,this指向這個實例創(chuàng)建的對象.
let animal=new Animal();
animal.name='動物1'
let animal1=new Animal();
console.log(animal1.name) //動物
//如果手動返回一個引用類型this就會指向這個引用類型
function Animal(){
this.name='動物'
//this指向返回這個對象
return {
a:2
}
}
let animal=new Animal(); //{a:2}
console.log(animal.name) //undefined
靜態(tài)屬性
function Animal(){
}
//靜態(tài)屬性
Animal.attr='12'
//靜態(tài)方法
Animal.say=function(){
return 11111
}
console.log(Animal.attr) //12
console.log(Animal.say()) //11111
原型prototype(每個類都有prototype,是一個對象)
function Animal(){
}
//公共方法 每個實例上都會帶有這方法
Animal.prototype.say=function(){
return 'say'
}
Animal.prototype.attr='attr'
let animal=new Animal();
let animal1=new Animal();
console.log(animal.say===animal1.say) //true 所有的實例都指向這個地址
console.log(animal.say()) //say
console.log(Animal.prototype) //Animal { say: [Function], attr: 'attr' }
_ _proto _ _ (實例 _ _ proto _ _ 指向所屬類的原型,用來查找屬性和方法)
console.log('123'.__proto__)
console.log({}.__proto__)
let num=123
console.log(num.__proto__)
let bol=true;
console.log(bol.__proto__)
//結(jié)果
//[String: '']
//{}
//[Number: 0]
//[Boolean: false]
//null undefined error
function Animal(){
}
Animal.prototype.say=function(){
return 'say'
}
Animal.prototype.attr='attr'
let animal=new Animal();
console.log(animal.__proto__);// Animal { say: [Function], attr: 'attr' } 指向?qū)嵗鶎兕惖脑蛯ο?// 查找順序先找自身 自身沒有再去查找所屬類的原型對象
console.log(animal.__proto__===Animal.prototype); //true
構(gòu)造函數(shù)constructor(prototype里包含一個constructor屬性)
function Animal(){
}
let animal=new Animal()
console.log(animal.__proto__.constructor)//[Function: Animal]
console.log(Animal.prototype.constructor) //[Function: Animal]
原理
無標(biāo)題.png
Object.prototype
無標(biāo)題.png
class Animal{
}
let animal=new Animal()
console.log(Animal.prototype.__proto__===animal.__proto__.__proto__) //true
console.log(Animal.prototype.__proto__===Object.prototype) //true
console.log(animal.__proto__.__proto__===Object.prototype) //true
console.log(Function.prototype.__proto__===Object.prototype) //true
console.log(Array.prototype.__proto__===Object.prototype) //true
console.log(Function.__proto__===Object.__proto__) //true
console.log(Object.prototype.__proto__) //null Object已經(jīng)是頂層了
繼承
1.實例屬性繼承
function Animal(name){
this.name=name
}
function Tiger(){
//this指向Tiger實例
Animal.call(this,'老虎')
}
let tiger=new Tiger();
console.log(tiger.name) //老虎
2.原型繼承
function Animal(name){
this.name=name
}
Animal.prototype.say=function(){
return 'say'
}
function Tiger(){
//this指向Tiger實例
Animal.call(this,'老虎')
}
//這種方式不可采 把Tiget的指向Animal.prototype會造成子類和父類同引用一個內(nèi)存地址 子類原型改變會影響父類
Tiger.prototype=Animal.prototype
//這樣不會改變子類的原型 先從子類查找 子類查找不到再去父類原型查找 不會污染父類 可以在自己子類原型增加方法
Tiger.prototype.__proto__=Animal.prototype
let tiger=new Tiger();
console.log(tiger.__proto__) //Tiger {}
console.log(tiger.say()) //say
2.1 object.create繼承
function Animal(name){
this.name=name
}
Animal.prototype.say=function(){
return 'say'
}
function Tiger(){
//this指向Tiger實例
Animal.call(this,'老虎')
}
//tiger.constructor指向的是父類
Tiger.prototype=Object.create(Animal.prototype)
//設(shè)置constructor tiger.constructor指向自己
Tiger.prototype=Object.create(Animal.prototype,{constructor:{value:Tiger}})
let tiger=new Tiger();
console.log(tiger.__proto__.constructor)
console.log(tiger.say())
2.2 模擬object.create
function Animal(name){
this.name=name
}
Animal.prototype.say=function(){
return 'say'
}
function Tiger(){
//this指向Tiger實例
Animal.call(this,'老虎')
}
function create(prototypes,constr){
function Fn(){
}
Fn.prototype=prototypes;
if(constr&&constr.constructor.value){
Fn.prototype.constructor=constr.constructor.value
}
return new Fn()
}
Tiger.prototype=create(Animal.prototype,{constructor:{value:Tiger}});
Tiger.prototype.say=function(){
return 'Tiger say'
}
let tiger=new Tiger();
console.log(tiger.__proto__.constructor) //[Function: Tiger]
console.log(tiger.say()) // Tiger say
console.log(Animal.prototype.say()) //say
object.create實現(xiàn)原理
無標(biāo)題.png
es6類(class)
class Animal{
}
let animal=new Animal();
console.log(animal)
靜態(tài)屬性(通過類自身調(diào)用)
class Animal{
static attrs='attrs'
static say(){
return 'say'
}
}
console.log(Animal.attrs) //attrs
console.log(Animal.say()) //say
class Animal{
static attrs='attrs'
constructor(){}
say(){
return 'say'
}
}
class Tiger extends Animal{
constructor(){
}
}
console.log(Tiger.attrs) //attrs
實例屬性
//沒有寫contructor情況下 增加屬性 會默認(rèn)執(zhí)行constructor把屬性添加到實例上
class Animal{
attrs='attrs' // 注意!!這里定義的不是在prototype上的屬性,而是給實例初始化的 實例屬性
}
let animal=new Animal();
console.log(animal.attrs)
class Animal{
constructor(){
this.attrs='attrs' //實例屬性
}
}
let animal=new Animal();
console.log(animal.attrs)
prototype(定義在類里 es6的原型是不可枚舉的)
class Animal{
constructor(){}
say(){
return 'say'
}
}
let animal=new Animal();
console.log(Animal.prototype.say) //[Function: say]
私有屬性(ES2020實驗草案中晰甚,增加定義私有類字段功能福扬,使用# ,私有屬性無法在類的外部調(diào)用)
//私有屬性
class Animal{
#privateValue="私有"
}
//私有方法
class Animal{
#privateValue(){
return 'private'
}
}
//私有靜態(tài)屬性
class Animal{
statci #privateValue="私有"
}
//可以在類的內(nèi)部使用脯倒,無法在類的外部使用
calss Animal{
#attrs='11111'
#bac(){
Animal.#attrs="22222"
}
}
console.log(Animal.#bac) //error
繼承(extexds)
//子類不寫constructor會默認(rèn)執(zhí)行父類的constructor
class Animal{
constructor(attrs){
this.attrs=attrs
}
say(){
return 'say'
}
}
class Tiger extends Animal{
}
let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.__proto__.say) //[Function: say]
//子類寫constructor,默認(rèn)走子類的構(gòu)造函數(shù)
class Animal{
constructor(attrs){
this.attrs=attrs
}
say(){
return 'say'
}
}
class Tiger extends Animal{
constructor(attrs){
super(attrs) //等同于 Animal.call(this,attrs) 不調(diào)用super會報錯
}
}
let tiger=new Tiger('tiger attrs');
console.log(tiger) //Animal { attrs: 'tiger attrs' }
console.log(tiger.__proto__.say) //[Function: say]
//子類和父類定義相同的方法愿吹,會先執(zhí)行子類 子類沒有才會向類查找
class Animal{
constructor(attrs){
this.attrs=attrs
}
say(){
return 'say'
}
}
class Tiger extends Animal{
constructor(attrs){
super(attrs) //等同于 Animal.call(this,attrs)
}
say(){
return 'tiger say'
}
}
let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.say()) // tiger say
//子類調(diào)用父類方法
class Animal{
constructor(attrs){
this.attrs=attrs
}
say(){
return 'say'
}
}
class Tiger extends Animal{
constructor(attrs){
super(attrs) //等同于 Animal.call(this,attrs)
}
say(){
return super.say() //等用于 Animal.prototype.say() //super===Animal.prototype
}
}
let tiger=new Tiger('tiger attrs'); //Tiger { attrs: 'tiger attrs' }
console.log(tiger.say()) //say
//靜態(tài)方法調(diào)用
class Animal{
constructor(attrs){
this.attrs=attrs
}
static say(){
return 'say'
}
}
class Tiger extends Animal{
constructor(attrs){
super(attrs) //等同于 Animal.call(this,attrs)
}
static say(){
return super.say() //super等用于Animal類 //super===Animal
}
}
console.log(Tiger.say()) //say
抽象類(只可繼承金踪,不可被實例化new)
1.new.target實現(xiàn)抽象類
class Animal{
constructor(){
//執(zhí)行兩次 1.new.target=[Function: Animal] 2.new.target[Function: Tiger]
if(new.target===Animal){
throw new Error('not new')
}
}
}
class Tiger extends Animal{
}
new Animal() //Error: not new
let tiger=new Tiger();
es5構(gòu)造函數(shù)模擬實現(xiàn)es6(原型屬性不可枚舉)
function handleConstructor(Constructor,protoProperties){
for(let i=0;i<protoProperties.length;i++){
let property=protoProperties[i]
Object.defineProperty(Constructor,property.key,{
//是否可以刪除
configurable:true,
//是否可以枚舉
enumerable:false,
...property
})
}
}
// console.log(Eat.prototype) //不可枚舉
function definePrototype(Constructor,protoProperties,staticPorto){
if(Array.isArray(protoProperties)){
handleConstructor(Constructor.prototype,protoProperties)
}
if(Array.isArray(staticPorto)){
handleConstructor(Constructor,staticPorto)
}
}
//es5的類 模擬es6的類 不可枚舉 Object.definprototype
let Animate=(function(){
function Animate(){
if(!(this instanceof Animate)){
throw new Error('not new')
}
}
//改變原型不可枚舉 babel編譯出es6的類就是這樣寫的
definePrototype(Animate,[
{
key:'say',value:function(){
console.log('say')
}
}
],[
//靜態(tài)屬性
{
key:'eat',value:function(){
return 'eat'
}
}
])
return Animate
})()
console.log(Animate) //[Function: Animate]
console.log(Animate.prototype) //Animate {}
console.log(Animate.eat()) //eat
instanceof
instanceof運算符是用來判斷一個構(gòu)造函數(shù)的prototype屬性所指向的對象是否存在另外一個要檢測對象的原型鏈上.
//obj instanceof Object 檢測Object.prototype是否存在與參數(shù)obj的原型鏈上.
function Person(){}
let p=new Person();
//p.__proto__==Person.prototype
console.log(p instanceof Person) //true
hasOwnProperty()操作符
hasOwnProperty檢查一個屬性是否是實例屬性.
//實例屬性
function Animal(){
this.name='name'
}
let animal=new Animal();
console.log(animal.hasOwnProperty('name')) //true
//不查看原型屬性,只看實例屬性
function Animal(){
}
Animal.prototype.name='123'
let animal=new Animal();
console.log(animal.hasOwnProperty('name')) //false
in操作符
有兩種方法能使用in操作符:單獨使用for-in循環(huán)使用.在單獨使用時牵敷,in操作符會通過對象能夠訪問給定屬性返回true,無論屬性存在(實例)還是(原型中).
function Animal(){
this.name='name'
}
let animal=new Animal();
console.log('name' in animal) //true
function Animal(){
}
Animal.prototype.name='123'
let animal=new Animal();
console.log('name' in animal) //true
function Animal(){
}
let animal=new Animal();
console.log('name' in animal) //false