原型链、面向对象

    技术2022-07-13  92

    一、原型链

    对于原型链,会从创建对象的方式、原型、构造函数、实例、原型链、instanceof 的原理、new 运算符这几个方面分析 对于创建对象的方式,如下所示:

    字面量的方式,如下所示: var o1 = {name: 'o1'}; var o2 = new Object({name: 'o2'}); 构造函数的方式,如下所示: var M = function (name) { this.name = name; }; var o3 = new M('o3'); Object.create 的方式,如下所示: var p = {name: 'p'}; var o4 = Object.create(p);

    对于原型、构造函数、实例、原型链这几个的关系,如下所示:

    在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象,使用原型对象的好处是所有对象实例共享它所包含的属性和方法原型链解决的主要是继承问题。每个对象拥有一个原型对象,通过 proto 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null(Object.proptotype.proto 指向的是null)。这种关系被称为原型链(prototype chain),通过原型链一个对象可以拥有定义在其他对象中的属性和方法prototype 是构造函数的属性,proto 是每个实例都有的属性,可以访问 [[prototype]] 属性,实例的 proto 与其构造函数的 prototype 指向的是同一个对象原型链是原型对象创建过程的历史记录,当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__ 隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构所有函数的__proto__都是指向Function的prototype,构造函数new出来的对象__proto__指向构造函数的prototype,非构造函数实例化出的对象或者对象的prototype的__proto__指向Object的prototype,Object的prototype指向null对于 instanceof的原理,instanceof主要用于判断某个实例是否属于某个类型,也可用于判断某个实例是否是其父类型或者祖先类型的实例。instanceof主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。 对于 new 运算符,如下所示:一个新对象被创建,它继承自 foo.prototype构造函数 foo 被执行,执行的时候,相应的参数会被传入,同时上下文 this 会被指定为这个新实例。new foo 等同于 new foo(),只能用在不传递任何参数的情况如果构造函数返回了一个对象,那么这个对象会去取代整个 new 出来的结果。如果构造函数没有返回对象,那么 new 出来的结果为步骤一创建的对象

    对于原型链的代码,如下所示:

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>原型链</title> </head> <body> <script type="text/javascript"> M.prototype.say = function () { console.log('say hi'); }; var o5 = new M('o5'); var new2 = function (func) { var o = Object.create(func.prototype); var k = func.call(o); if (typeof k === 'object') { return k; } else { return o; } }; </script> </body> </html>

    二、面向对象

    对于面向对象,分为类与实例和类与继承,类与实例包括类的声明、生成实例,类与继承包括实现继承和继承的几种方式。 对于类的声明,如下所示:

    类的声明,代码如下 var Animal = function () { this.name = 'Animal'; }; es6 中 class 的声明 class Animal2 { constructor () { this.name = 'Animal2'; } }

    对于类与继承的实现及方式,如下所示:

    借助构造函数实现继承,但是缺点是 child1 没有继承 Parent1 原型对象的方法,代码如下所示: function Parent1 () { this.name = 'parent1'; } Parent1.prototype.say = function () { }; function Child1 () { Parent1.call(this); this.type = 'child1'; } console.log(new Child1(), new Child1().say()); 借助原型链实现继承,但是缺点是 s1 改变的东西,s2 也会看到,代码如下所示: function Parent2 () { this.name = 'parent2'; this.play = [1, 2, 3]; } function Child2 () { this.type = 'child2'; } Child2.prototype = new Parent2(); var s1 = new Child2(); var s2 = new Child2(); console.log(s1.play, s2.play); s1.play.push(4); 组合方式,Parent3 初始化了两次,指向 Parent3 构造函数,代码如下所示: function Parent3 () { this.name = 'parent3'; this.play = [1, 2, 3]; } function Child3 () { Parent3.call(this); this.type = 'child3'; } Child3.prototype = new Parent3(); var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play, s4.play); 组合继承的优化一,但是缺点是指向 Parent4 的构造函数,无法区分实例是由父类创建的,还是子类创建的,代码如下所示: function Parent4 () { this.name = 'parent4'; this.play = [1, 2, 3]; } function Child4 () { Parent4.call(this); this.type = 'child4'; } Child4.prototype = Parent4.prototype; var s5 = new Child4(); var s6 = new Child4(); console.log(s5, s6); console.log(s5 instanceof Child4, s5 instanceof Parent4); console.log(s5.constructor); 组合继承的优化二,代码如下所示: function Parent5 () { this.name = 'parent5'; this.play = [1, 2, 3]; } function Child5 () { Parent5.call(this); this.type = 'child5'; } Child5.prototype = Object.create(Parent5.prototype);
    Processed: 0.010, SQL: 9