js面向对象new 类 原型笔记

本文详细讲解了JavaScript中的new运算符的工作原理,包括构造函数的作用、工厂模式与构造函数的对比、原型对象的特性以及原型链的构成。此外,还介绍了instanceof操作符的原理和实现,以及如何仿写new运算符。文章最后讨论了面向对象的继承机制,深浅拷贝的区别,以及ES6中类的实现和继承。通过对这些概念的深入理解,有助于提升JavaScript编程能力。
摘要由CSDN通过智能技术生成

一、new运算符特点 

        1.执行函数       new fn / new fn()

        2.自动创建一个空对象        new Tab();  --->this

        3.把创建的对象指向另外一个对象        let tab = new Tab();   返回的是obj

        4.把空对象和函数里的this衔接起来         this指向实例化对象

        5.隐式返还this( this的创建与返回是隐性的 )

1)构造函数的作用?

1、使类的成员变量有合适的初值

2、构造函数的作用就是用来实例化的

3、名字和类名相同,没有返回值,不需要用户调用。

4、调用是在创建该类的对象的时候,由编译器自动调用

5、拷贝构造函数是指类的对象在创建的时候,能使用已有的对象初始化它。

      或者在类的对象作为函数形参 / 函数返回值的时候,作为复制的机制需要自动拷贝类型值。

二、new简化工厂模式 = 构造函数

工厂模式:     没有constructor

        function tab(){

                let obj = {};    //new Tab() 时自动创建一个空对象,并且指向this,此条不需要

                obj.hobby = function(){ }     //new把obj指向this,可改为this.hobby

                return obj;        //隐式返还this,这条不需要了

        }

        let tab = new Tab();      //this指向obj即指向tab

构造函数:

        function Tab(){

                this.hobby = function(){ }        //new把obj指向this,可改为this.hobby

        }

        let tab = new Tab();

        tab.hobby();

构造函数更简洁

三、构造函数的原型(对象)

实例化对象由两部分构成:   构造函数、原型       共用一个this  

        function Tab(){            

                this.name = “张三”;

        }

        Tab.num = 10;           //ES5的静态属性

        Tab.prototype.pFor = function(){       

                console.log("pFor", this.name);

         }

        Tab.prototype.hobby = function(){        

                console.log("hobby", this.name);

         }

         let tab = new Tab(); 

         console.log(tab.constructor === Tab);     //true

         console.log(tab instanceof Object);        //true;
         console.log(tab instanceof Tab);             //true;

instanceof专门比较一个对象是否为某个构造函数的实例

ES6写法:

    不能这样写:

        Tab.prototype = {        

              ( constructor:tab,)   //需要加上 

                pFor(){

                        console.log("pFor", this.name);

                },

                hobby(){

                        console.log("hobby");

                }

         }

constructor:

每个原型都有一个预定义属性 constructor , 指向构造函数

这样写会覆盖原本  constructor  属性,原型断了

可用于判断构造函数

        let str = "abd"

        if(str.constructor === String){           //String构造器

                console.log("字符串")

        }


 回到原型

        let tab1 = new Tab();  

        let tab2 = new Tab();

        console.log(tab1.pFor === tab2.pFor)       //true 这里的tab1、tab2相当于拿到指针

        console.log(tab1.__proto__ === Tab.prototype);      //true

1、约定首字母大写 

2、原型是一个对象,属性在构造函数(this.name)上,方法在原型上 

属性:是简单数据类型,不存在参数问题

原型本身是一个对象,对象有原型

1)没有原型可以是对象吗?

可以      Object.create(null)  没有构造函数和原型

Object.create(Object.prototype,{ })  与 { } 相同

 creat(null)的使用场景

1、for..in循环会遍历对象原型链上的属性,使用create(null)就不必对属性进行检查了,当然,我们也可以直接使用Object.keys[]

2、想节省hasOwnProperty带来的一丢丢性能损失并且可以偷懒少些一点代码的时候

3、自己定义hasOwnPropertytoString方法,不必担心将原型链上的同名方法覆盖

 详解Object.create(null)_嘿嘿-CSDN博客

2)为什么通用函数要放在原型?

1、方法放在构造函数,相当于工厂模式,每次调用都将在堆内存开辟新的空间

2、原型的作用解决了重复开辟新内存的冗余

四、原型链

  • 原型本身是一个对象,也由本身和原型构成  
  •  先找本身的属性,如果没有就找原型链里的

arr.__proto__ == Array.prototype
true
arr.__proto__.__proto__ == Object.prototype
true
arr.__proto__.__proto__.__proto__ == null
true

function Drag(){   

        this.ele = "11";

}

Drag.prototype.ele = "22";         

相当于:

        let obj = new Object();

        Object.prototype.ele = "33";

let drag = new Drag( );

console.log(drag.ele);         //先返回本身的ele,没有的话再返回原型里的ele

console.log(Object.prototype.__proto__);        //返回null,也是一个对象

// 当"22"也不存在时,返回“33”

 同一个原型

 

面试题:instanceof的原理是什么?代码实现

  • 专门比较一个对象是否为某个构造函数的实例,用它来检测对象的类型更可靠
  • 不仅判断和构造函数是否有关,还判断是否和原型链有关
  • 如果一个实例化对象A沿着原型链能找到B.prototype  =>  A instanceof B为true
const instanceofA = (A, B) =>{
    let p = A;
    while(p){
        if(p === B.prototype) return true;
        p = p.__proto__;
    }
    return false;
}
instanceofA([],Array)
instanceofA([],Object)
instanceofA(1,Number)

五、仿写new运算符

function nyNew (  constructor, ...arg  ){         //不定参argument,用rest参数传

        let obj = { };                                     //1、创建一个新的对象

        constructor = [].slice.call( obj,...arg);         //2、将构造函数作用域赋给新的对象(即this指向新对象),arg传参 

        obj.__proto__ = constructor.prototype;  //3、构造函数的原型指向对象的原型

        return obj;

let tab = myNew(Tab); 

console.log(tab.name,tab.pFor);                         //能拿到构造函数中的属性、方法

六、面向对象

继承 

function Son(height){

        Dad.call(this,height)

        Dad.apply(this,[height])

        Dad.bind(this)(height)        //传参上面的区别

}

但是子拿不到父的原型(方法)     

解决办法:

        Son.prototype = Dad.prototype;       

问题:若子类覆盖方法,父类也会被改变(浅拷贝(引用)对象是引用数据类型,这里只传地址)

           解决办法——组合继承:用于重写子类方法,不影响父类

                let Link = function(){ };        //新的构造函数

                Link.prototype = Dad.prototype;       

                Son.prototype = new Link();          //问题:预定义属性constructor

解决办法: Son.prototype.constructor = Son;  //因为constructor被覆盖了

七、深浅拷贝 / 传值和传址

引用/复杂数据类型 如何实现深拷贝(重新开辟地址传值)?

1、let obj2 = JSON.parse(JSON.stringify(obj))        //序列化开辟新地址

 缺陷:丢失undefined和fn类型 


2、function deepCopy(obj){

        let newObj = Array.isArray(obj)? [ ] : { };

        for(let i in obj){         

                if(obj.hasOwnProperty(i)){     //2、是否为自身属性(不是原型)返回true

                        if(typeof obj [ i ]  === “object”){

                                if( obj [ i ]  === null)   newObj [ i ]  =  null;    //1、解决缺陷问题

                                else   newObj [ i ]  = deepCopy(obj [ i ] );

                        }

                        else{

                                newObj [ i ] = obj [ i ] ;

                        }

                }

        }        

        return newObj;

}

缺陷1:null类型会被typeof当成对象

缺陷2:for in 不仅循环对象,还会把对象原型里的属性和方法循环

八、ES6类(实现层面上是方法,理解为类,并不是真正的类)

动态属性 constructor 和 动态方法hobby()只和实例化对象有关,和函数无关

静态属性 height 和 静态方法test()只和类有关 === 实例化对象drag没有height这个属性

继承也是同样的规则!!

class Drag{         //类名大写              //是一个function,不是object

        static height =  “178cm”;     //静态属性 属于类的属性

        static test(){ };                   //调用Drag.test(); 

        constructor(age){                  //预定义属性

                this.name = "张三";

                this.age = 20;

        }

        hobby(){           

                ......

        }

或者将第二行换成 Drag.height =  ”178cm“

let drag = new Drag();                    //动态属性 用对象调用

console.log(Drag.height)


同时属于类和对象的:

Promise.resolve()                          // resolve属于类,不属于对象,无需实例化

继承

class LimitDrag extends Drag{             //ES6的继承没有es5传址的问题

        constructor(age){

                super(age);                      //一定要调用super

        }

        hobby(){                                   //重写父类hobby方法,会覆盖父方法

                super.hobby();                 //同时拿到父类子类的逻辑

                console.log(”111“);

        } 

}

let drag = new LimitDrag(20)           //先传入子类,通过super传入父类

LimitDrag.height;                                  //可以获取

九、包装对象 / 判断类型

arr.push()          arr 有原型,原型里有 push 方法

基本数据类型 string number boolean 没有原型

let str = new String(”a“)          //生成实例化对象才有


let str = "a"

let arr = str.split(“ ”)

相当于

function mysplit(str,method,arg){

        let temp = newString(str);       // 自动生成临时对象

        return  temp[ method ] (arg);          //对象原型里有方法,相当于temp调取split

        // return 完会销毁temp包装对象

}

let arr = mysplit(str,“split”,“ ”)


判断类型常用方法

js判断对象类型 - sunmarvell - 博客园

hasOwnProperty()           判断是否为对象自身属性

isPrototypeOf                       判断原型链对象是否存在于指定对象实例中

constructor                           判断实例化对象指向

function Person(){

        this.name = "a";

}                // Person.prototype.constructor  默认属性       所以写原型时不能覆盖

let a = new Person();

a.constructor === Person;        //true

let arr = [ ];

arr.constructor === Array;         //true

 instanceof            不仅判断和构造函数是否有关,还判断是否和原型链有关

掘金

let arr = [ ], str = "xxx"

str instanceof String;                  //false

arr instanceof Array;                  //true

arr instanceof Object;                //true

arr.constructor ===  Object;         //false

typeof                   也会从原型链上判断

typeof arr         //返回object     

typeof null        //返回object

Object.prototype.toString.call()           精确判断数据类型

Object.prototype.toString.call(arr);              //返回 [ object Array ] 

Object.prototype.toString.call(obj);              //返回 [ object Object ] 

res === "[ object Array ]"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值