最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入理解JS面向对象的new、原型、原型链

    正文概述 掘金(我的幸运7)   2021-02-02   494

    面向对象编程

    编程思想

    面向对象

    JS中的面向对象

    new关键字

    function sum(a,b){
        this.a = a;	// 通过this给实例对象添加属性
        this.b = b;
        this.total = a + b
        return a + b;
    }
    
    sum(1,1);		// 2
    new sum(1,1);	// {a:1,b:1,total:2}
    

    深入理解JS面向对象的new、原型、原型链

    1. 和普通函数执行一样,形成执行上下文EC、AO、作用域链等
    2. new操作符会在函数执行前创建一个对象
    3. 在执行上下文初始化环境时将this指针指向刚刚通过new创建的对象
    4. 执行上下文进栈执行时,通过this指针给new创建的实例对象设置‘"私有"属性、方法
    5. return 时,检测返回值,如果没有返回值或者返回值是一个原始数据类型则返回通过new创建的那个实例对象;如果有返回值并且返回值是引用数据类型,则返回该引用数据

    模拟实现new方法

    /*
     *  Func[function]:类
     *  params[array]:基于剩余运算符获取传递的实参,都是给类传递的实参
     */
    function _new(Func,...params){
        // 1. 创建实例对象
        let obj = {};
        obj.__proto__ = Func.prototype;
        // 2. 将类作为普通函数执行,基于call方法强制改变this的指向,将this指向新创建的实例对象obj
        let result = Func.call(obj,...params);
        // 3. 当返回的结果为引用数据类型时,则直接返回该值,否则返回obj
        if(result !== null && /^(object|function)$/i.test(typeof result)) return result;
        return obj;
    }
    
    function _new(Func,...params){
        let obj = Object.create(Func.prototype);
        let result = Func.call(obj,...params);
        if(result !== null && /^(object|function)$/i.test(typeof result)) return result;
        return obj;
    }
    
    Object.create = function create(obj){
        if(obj === null || typeof obj !== 'object'){
            throw new TypeError('Object prototype may only be an Object');
        }
        function fn(){};
        fn.prototype = obj;
        return new fn();
    }
    

    原型及原型链

    类和原型

    • 所有函数都会天生自带一个属性:prototype原型(显示原型)
    • prototype属性默认指向一个对象数据类型
    • 在对象中存储的是供实例调用的所有公共属性和方法
    • 类的原型对象上默认有个属性:constructor构造函数,属性值指向当前类本身

    深入理解JS面向对象的new、原型、原型链

    实例和原型链

    • __proto__ 属性的属性值:当前实例对象所属类的原型(prototype),及 实例.__proto__ === 类.prototype

    深入理解JS面向对象的new、原型、原型链

    类、原型、实例之间的关系

    function Fn(){
        this.x = 100;
        this.y = 200;
        this.getX = function(){
            console.log(this.x);
        }
    }
    Fn.prototype.getX = function(){
        console.log(this.x);
    }
    Fn.prototype.getY = function(){
        console.log(this.y);
    }
    let f1 = new Fn;
    let f2 = new Fn;
    
    console.log(f1.getX === f2.getX);		// false
    console.log(f1.getY === f2.getY);		//true
    console.log(f1.__proto__.getY === Fn.prototype.getY);	// true
    console.log(f1.__proto__.getX === f2.getX);				// false
    console.log(f1.getX === Fn.prototype.getX);				// false
    console.log(f1.constructor);							// Fn
    console.log(Fn.prototype.__proto__.constructor);		// Object
    f1.getX();					// 100
    f1.__proto__.getX();		// undefined
    f2.getY();					// 200
    Fn.prototype.getY();		// undefined
    

    深入理解JS面向对象的new、原型、原型链

    • 实例上的函数执行时,通过this用到某个属性,首先看是否是自己实例的私有属性,如果是自己的私有属性,那么就直接操作自己的私有属性
    • 如果不是自己的私有属性,会默认基于__proto__找所属类的原型上的公共属性和方法
    • 如果所属类的原型上也没有,则会继续基于原型链__proto__继续向上查找,直到找到Object.prototype上为止

    原型重定向

    原型重定向带来的问题

    • 新定向的原型对象上,没有constructor属性,结构不完善
    • 浏览器默认生成的原型对象会因为缺少引用而被释放掉,可能会导致原始加入的属性和方法丢失掉
    • 内置对象是不允许被重定向的,防止把内置对象的方法丢失
    function Fn(){
        this.x = x;
        this.y = y;
    }
    Fn.prototype.getX = function(){};
    Fn.prototype = {        //重定向的新对象,没有constructor,也没有getX了
        getY:function(){}
    };
    let f1 = new Fn;
    

    那原型重定向给我们带来了这么多弊端,那我们为什么还要做原型重定向呢?

    给原型批量添加方法

    function Fn(){
        this.x = x;
        this.y = y;
    }
    Fn.prototype.getX = function(){};
    Fn.prototype.getY = function(){};
    Fn.prototype.getZ = function(){};
    
    function Fn(){
        this.x = x;
        this.y = y;
    }
    let A = Fn.prototype
    A.getX = function(){};
    A.getY = function(){};
    A.getZ = function(){};
    
    function Fn(){
        this.x = x;
        this.y = y;
    }
    
    Fn.prototype.z = 300;
    Fn.prototype = Object.assign(Fn.prototype,{
        getX(){},
        getY(){},
        getZ(){},
    })
    

    基于内置类原型拓展方法

    • 写在内置类原型上的方法,实例在调用的时候实例.xxx()
      • xxx()方法执行
      • 执行主体this是要操作的实例
    • 向内置类原型上拓展方法的目的是,将一些公共的属性和方法拓展到内置类的原型上,这样调用起来就比较的方便
    • 自定义属性或方法名一定要与原型上本来就存在的属性和方法名区分开来,避免自己的方法将原型的方法覆盖
    let arr = [1,1,2,3,4,2,3,1,4,4]
    
    // 1.方法一
    function unique(arr){
        return Array.from(new Set(arr));
    }
    // 2.方法二
    Array.prototype.unique = function unique(){
        //保证this是一个数组
        if(!Array.isArray(this)) throw new TypeError('确保操作的是一个数组');
        return Array.from(new Set(this));
    }
    
    unique(arr)    //=>[1,2,3,4]
    arr.unique();   //=>[1,2,3,4]
    

    面试题

    let n = 10;
    let m = n.plus(10).minus(5);
    console.log(m)  //=>15 (10+10+-5)
    
    Array.prototype.plus = function plus(num){
        return this + num;
    }
    Array.prototype.minus = function minus(num){
        return this - num;
    }
    

    函数的三种角色

    Function内置类

    深入理解JS面向对象的new、原型、原型链

    虽然函数的原型指向的不是一个对象,但是这个匿名空函数的表现却和对象是一样的,没有任何区别,而且这个函数没有函数自带的属性prototype却存在对象的__proto__属性。

    虽然Function.prototype指向的是一个匿名的空函数,但是我们却同样能够通过函数的实例来调用Function原型上的公共属性和方法,从这我们能够看出函数其实也是一种对象。

    函数的三种角色

    函数的三种角色之间没有必然的联系

    终极原型链

    深入理解JS面向对象的new、原型、原型链

    1. 所有的内置类都是Function的实例(包括Object)
    2. Function本身,既是Object的实例也是Function的实例
    3. Object本身,既是Object的实例也是Function的实例
    4. Object.__proto__.__proto__ === Object.prototype
    5. Function.__proto__ === Function.prototype
    6. 在Function.prototype上有call、apply、bind三种改变this的方法,所有函数都能调用者三个方法

    阿里面试题

    function Foo() {
        getName = function () {
            console.log(1);
        };
        return this;
    }
    Foo.getName = function () {
        console.log(2);
    };
    Foo.prototype.getName = function () {
        console.log(3);
    };
    var getName = function () {
        console.log(4);
    };
    function getName() {
        console.log(5);
    }
    Foo.getName();          // 2
    getName();              // 4
    Foo().getName();        // 1
    getName();              // 1
    new Foo.getName();      // 2
    new Foo().getName();    // 3
    new new Foo().getName();    //3
    

    参考资料

    • 《this--函数的执行主体》
    • 《JavaScript函数的存储机制和运行原理》

    起源地下载网 » 深入理解JS面向对象的new、原型、原型链

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元