编程技术文章分享与教程

网站首页 > 技术文章 正文

js回忆录(4)——对象,构造函数

hmc789 2024-11-26 03:40:15 技术文章 2 ℃

1.对象 && 构造函数

js是一门基于对象的语言,里边所有的数据类型都可以当对象使唤(当然null和undefined除外),当我们在v8引擎里声明一个对象时会发现每个对象属性里边都有这个东西:


在说明这个属性之前需要先说明一点就是,声明一个对象和new Object 生成对象的结果是一样的,即,所有对象都是Object的实例,每一个实例都会继承它的构造函数上的prototype的属性,而上面的_proto_就是指向这个原型对象的,也就是说:

实例._proto_ = 构造函数.prototype

每个对象上查找属性时要走的流程大体是,查查自己有没有,查查自己的构造函数的prototype上有没有,再查查构造函数的prototype的构造函数的prototype上有没有,依次查找,如此变成了原型链。普通对象上关于原型只有一个_proto_ 属性指向它的构造函数的原型(原型对象),而在一个函数对象上则多了一个prototype属性,这个属性就是用于继承,创建子类共有的属性,方法的比较重要的一个属性。

函数初始化之后原型对象上有constructor和_proto_两个属性,一个指向这个函数一个指向它自己的构造函数的prototype,一般来说是Object.prototype。

构造函数实在是js里边一个有趣的东西,一个函数是不是构造函数不是取决于它的定义,而是取决于它的调用方式。构造函数不需要return语句 ,甚至调用的时候都不需要(),这一点就比较像php了。生成的实例对象私有属性由构造函数定义,共有属性由构造函数的prototype定义。

2.关于JS里的继承

继承其实是一个面向对象的编程语言的概念,JS天生的短板,一般面试的时候回答继承时怎么做,就只是简单的 子类.prototype = 父类.prototype,好了合格了,但是从实际出发,事情远远没有这么简单,首先是constructor的属性得改,然后得考虑到JS引用类型的特性,然后得留下子类已经定义的属性,防止被父类覆盖,所以一个合理的做法是深拷贝,

function inherit( sub,sup ){
    let temp = deepCopy(sup.prototype);//深拷贝父类prototype
    for( let key in temp  ){
        if( sub.prototype[key] !==null || sub.prototype[key] !== undefined){//子类中如果已经存在对应属性,则不作操作
        }else{
            sub.prototype[key] = temp[key]
        }
    }
    function deepCopy(obj){
        let result;
        if(JSON){
            result = JSON.parse(JSON.stringify(obj))
        }else{
            for( let key in obj  ){
                result[key] = typeof( obj[key] ) !== 'object'? obj[key] : deepCopy(obj[key])
            }
        }
        return result
    }
}

如果已经确定子类中没有要保留的属性,可以用一下司徒正美大佬的继承方法,这是他的anu框架里边的源码的一个方法,很妙啊,我做了适当删减。

function inherit(SubClass, SupClass) {
    function Bridge() {}
    Bridge.prototype = SupClass.prototype;
    
    var fn = SubClass.prototype = new Bridge();

    fn.constructor = SubClass;
}

----------------------------------------2018-5-15更新-----------------------------------------------------------------

本来以为前面几个继承已经实现的差不多了,然而前几天和某位大佬讨论一番之后,却发现上边那个继承的实现方式并不是很完美,首先,js基于原型链的继承其一是出于性能考虑的,如果采用上面我深拷贝的那种方式实现的话,相当于是将父类的共有方法又拷贝了一份,不是很完美,然后这几天结合自己的思考和es6提供的一些方法,可以使用下面的方式更新,方法1:

function parent(){}
function son(){}
Object.setPrototypeOf(son.prototype,parent.prototype);

Object.setPrototypeOf 方法可以设置一个对象的原型对象,进而通过这种方式实现继承。第二种方法是改变__proto__指向,即:

son.prototype.__proto__ = parent.prototype;

然而,以上两种方式mdn都不是很推荐,具体原因可以看看上边setPrototypeOf的链接,这样做的后果就是牵涉到性能问题了,而比较推荐的方法是使用Object.create这个es5就有的方法:

function parent(){}
function son(){}
son.prototype = Object.create( parent.prototype )

Object.create的第一个参数即为新创造的对象的原型对象。

因此通过这种方法实现继承,不光保证了性能,也可以动态的继承父类的公有属性。

Tags:

标签列表
最新留言