继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

面向对象的小九九

哔哔one
关注TA
已关注
手记 340
粉丝 93
获赞 543

面向对象

本人能力有限,有误请斧正

本文旨在复习面向对象(不包含es6)

本文学习思维

  1. 创建对象的方式,获取对象属性

  2. 构造函数,构造函数的new 做了什么

  3. 原型与原型对象

  4. 原型链

  5. 继承(借用构造继承、原型继承、组合继承、寄生组合继承)


获取对象属性的三个方法

  1. for...in..

  2. Object.keys() ie9以上放心使用
    keys的支持

  3. Object.getOwnPeropertyNames 会把所有属性枚举出来(如数组的length)

创建对象的方法

  1. 字面量   var o = {};

  2. 构造函数 var o = new Object()

  3. Object.create() var o = Object.create( )

object.create()有两个参数第一个是要新创建对象的原型对象, 第二个可选:自己定义的属性,需要配置麻烦一般不用,返回一个新对象,带着指定的原型对象和属性

通过给Object.create()参数传null可以获得一个纯净的没有原型的对象。原因是null是原型链的链末

输出查看原型链发现create在创造一个对象会存在传入的属性与方法
这个方法太适合继承了,他会直接继承传入的属性和方法
通过测试传入{}时,也会存在Object.__proto__ 指向 Object.__proto__;

// Object.create() 实现方式 // 实际这个是原型式继承的核心var object = function(proto){    var F = function(){}; // 创建一个对象
    F.prototype = proto; //变量原型指向传入对象
    return new F();
}

构造函数、实例、原型、原型链

参考资料:MDN-继承与原型链

构造函数:

  1. 是一个函数

  2. 首字母大写

  3. 使用new关键字创建

构造函数(函数声明或者函数表达式)本质还是函数,只是用来创建对象,还有个惯例就是首字母大写

// 构造函数function Person(){}// 调用构造函数,创建对象var p1 = new Person();

为什么说构造函数特殊呢,首先聊聊new关键字

参考:

  1. MDN-new运算符

  2. 高程3--p145

  3. 阮一峰博客

通过new关键字可以创建新对象!

在new一个对象的时候做了什么事?4个步骤(高程3)

  1. 创建一个新对象

  2. 将构造函数的作用域赋给新对象 (因此this就指向了这个新对象)

  3. 执行构造函数中的代码(为这个新对象添加属性)

  4. 返回新对象(如果构造函数中返回了其他对象,则返回其他对象)

MDN简化版,只讨论过程,无法传参

    // MND简化
    
    /*
    一个继承自 Foo.prototype 的新对象被创建。
    
    使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。
    new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
    
    由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。
    (一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
    */
    
    var Foo = function(){};    var _new2 = function(fn){        var o = Object.create(fn.prototype);        var k = fn.call(o);        if(typeof k === 'object'){            return k;
        }else{            return o;
        }
    }    var f = _new2(Foo);

阮老师的版本可以传参,而且很详细了

    function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {      // 将 arguments 对象转为数组
      var args = [].slice.call(arguments);      // 取出构造函数
      var constructor = args.shift();      // 创建一个空对象,继承构造函数的 prototype 属性
      var context = Object.create(constructor.prototype);      // 执行构造函数
      var result = constructor.apply(context, args);      // 如果返回结果是对象,就直接返回,否则返回 context 对象
      return (typeof result === 'object' && result != null) ? result : context;
    }    // 实例
    var actor = _new(Person, '张三', 28);

实例是什么

构造函数是对一个对象的抽象描述,实例则是对象的具体表现

原型对象(prototype)

好吧!大boss出场,都说javaScript最具有特色的就是原型

参考:

  1. 高程3 --p147

原型是什么?(高程3)

我们创建的每个函数都有一个prototype(原型) 属性,这个属性是一个指针,指向一个对象

  1. 函数的属性

  2. 原型指向一个对象

理解原型对象 高程3 -- p148有兴趣可以去读一下

无论什么时候只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性将指向函数的原型对象。在默认情况下,所有的原型对象会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所有函数的指针。通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法

按照书上的理解:


webp

MDN-prototype示意

简述:([[Prototype]] === __proto__

  1. 所有构造函数有一个属性指向原型对象(prototype)

  2. 所有由构造器生成的实例对象中有个__poroto__指向原型对象

  3. 原型对象中都有一个constructor的属性,指向构造函数

        var Person = function() {};
        Person.prototype.age = 1;        var p = new Person();        var p2 = new Person();        console.log(p.__proto__ === Person.prototype); // true
        console.log(p2.__proto__ === Person.prototype); // true
        console.log(Person === Person.prototype.constructor); // true

原型链

原型链就是在查找到某个属性或者方法不断向上查找的一个过程

MDN-非常具有代表的简化原型链

// 让我们假设我们有一个对象 o, 其有自己的属性 a 和 b:// {a: 1, b: 2}// o 的 [[Prototype]] 有属性 b 和 c:// {b: 3, c: 4}// 最后, o.[[Prototype]].[[Prototype]] 是 null.// 这就是原型链的末尾,即 null,// 根据定义,null 没有[[Prototype]].// 综上,整个原型链如下: // {a:1, b:2} ---> {b:3, c:4} ---> nullconsole.log(o.a); // 1// a是o的自身属性吗?是的,该属性的值为1console.log(o.b); // 2// b是o的自身属性吗?是的,该属性的值为2// 原型上也有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)"console.log(o.c); // 4// c是o的自身属性吗?不是,那看看原型上有没有// c是o.[[Prototype]]的属性吗?是的,该属性的值为4console.log(o.d); // undefined// d是o的自身属性吗?不是,那看看原型上有没有// d是o.[[Prototype]]的属性吗?不是,那看看它的原型上有没有// o.[[Prototype]].[[Prototype]] 为 null,停止搜索// 没有d属性,返回undefined

webp

MDN原型简图.png


再用对象表示一个

// 属性遮蔽function Person() {    this.name = '111';
}
Person.prototype.name = '222';var p1 = new Person();console.log(p1.name); // 111console.log(p1.__proto__.name); // 222var p2  = new Person();console.log(p2.age);

现在要查找p2.age属性

  1. 实例对象中有没有?没有

  2. 实例对象通过__prope__找到原型对象,原型对象中有么?没有

  3. 找到Object的原型中查找有么?没

  4. 找到null这个对象,作为作用域的链末,也没有,这个值就是undefined

webp

prototype原型链与属性屏蔽.png

属性屏蔽就是找到了就不会再找了(实例上的属性>原型链上的属性),实际还是存在

几种能遇到的操作符

  1. in操作符

  2. isPrototypeOf()

  3. Object.getPrototypeOf()

  4. instanceof (对象)

  5. typeof

in操作符

如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。语法:prop in object

isPrototypeOf()

isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。

function Foo() {}function Bar() {}function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);var baz = new Baz();console.log(Baz.prototype.isPrototypeOf(baz)); // trueconsole.log(Bar.prototype.isPrototypeOf(baz)); // trueconsole.log(Foo.prototype.isPrototypeOf(baz)); // trueconsole.log(Object.prototype.isPrototypeOf(baz)); // true
instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例。

instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。(判断他们的地址指向是否一致)。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。

几点instanceof的注意

  1. 用于对象(由于instanceof的原理)

  2. 与null有关要注意

//instanceof判断会失真var obj = Object.create(null);typeof obj // "object"Object.create(null) instanceof Object // false//null作为一个特殊的Object却不属于Object创建的实例,null原型链的链末undefined instanceof Object // falsenull instanceof Object // false// instanceof 用于对象var str = '1'var str2 = new String('2');
str instanceof String // falsestr2 instanceof String // true
typeof
typeof 1 //numbertypeof '' // stringtypeof undefined //undefinedtypeof true // booleantypeof function(){} // functiontypeof {}  // objecttypeof []  // objecttypeof null // objecttypeof Symbol() //symbol ES6

继承:

继承有几种

webp

高程3继承@coderlfy.png

我用我总结了一些思维导图

  1. 创建对象

  2. 继承的优缺点

这里把继承的几种方式罗列出来方便查阅,以下大多是代码,简易的我总结在思维导图中了

1.原型(链)继承

关键点是要打通原型链
由于原型对象是函数初次创建就会存在的对象,所以会共享
共享就会存在共享问题

优点:

  1. 共享属性与方法

  2. 可以通过instanceof来判断关系

缺点:

  1. 共享问题

  2. 不能传递参数

        // 父类
        function SuperType() {            this.property = true;
        }
        SuperType.prototype.getSuperValue = function () {            return this.property;
        }        
        // 子类
        function SubType() {            this.subproperty = false;
        }        // 继承父类 打通原型链
        SubType.prototype = new SuperType();
        SubType.prototype.getSubValue = function () {            return this.subproperty;
        }        
        var instance = new SubType();        console.log(instance.getSuperValue()); // true
借用构造函数

关键在于环境变量(this)的指向,由于每次创建都会创建一个新的this所以会拥有自己的属性与方法,由于是改变this指向所以无法共享原型对象

优点:

  1. 私有属性与方法

  2. 可以传参数

缺点:

  1. 引用类型,重复创建,冗余浪费内存

  2. 无法共享

  3. 无法判断关系

        // 父类
        function SuperType() {            this.colors = ['red'];
        }        // 子类
        function SubType() {            // 继承父类
            SuperType.call(this);
        }        var instance1 = new SubType();
        colors.push('black');
        cosnole.log(instance1.colors); // red,black
        var instance2 = new SubType();        console.log(instance2.colors); // red
组合式继承

组合了原型继承与借用构造函数继承继承了优点,但是由于组合,所以创建了两次对象,造成轻微的浪费空间

优点:

  1. 私有属性和方法

  2. 共享属性与方法

  3. 可以确认实例与构造函数之间的关系

缺点

  1. 造成内存的冗余浪费

        // 父类
        function SuperType(name) {            this.name = name;            this.colors = ['red'];
        }
        SuperType.prototype.sayName = function () {            console.log(this.name);
        }        // 子类
        function SubType(name, age) {            // 继承属性
            SuperType.call(this, name);            this.age = age;
        }        // 继承方法
        SubType.prototype = new SuperType();
        SubType.prototype.sayAge = function () {            console.log(this.age);
        }        var instance1 = new SubType('name1', 1);
        instance1.colors.push('black');        console.log(instance1.colors); // red,black
        instance1.sayName(); // name1
        instance1.sayAge(); // 1

        var instance2 = new SubType('name2', 2);        console.log(instance2.colors); // red
        instance2.sayName(); // name2
        instance2.sayAge(); // 2
寄生组合继承

寄生组合继承是把原型继承给改掉,实际上就是想要父级的原型链,不一定要创建对象所以有了寄生组合继承,该继承是目前最完善的继承方式

        // 寄生继承
        function inheritPrototype(subType, superType) {            var prototype = Object.create(superType.prototype);
            prototype.constructor = subType;
            subType.prototype = prototype;
        }        // 父类
        function SuperType(name) {            this.name = name;            this.colors = ['red'];
        }
        SuperType.prototype.sayName = function () {            console.log(this.name);
        }   
        // 子类继承
        function SubType(name, age) {
            SuperType.call(this, name); 
        }
        inheritPrototype(SubType, SuperType);
        SubType.prototype.sayAge = function () {            console.log(this.age)
        }



作者:coderLfy
链接:https://www.jianshu.com/p/c08fde09a65d


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP