前端复盘笔记——原型链

前端复盘1——原型链

  1. 没有原型的对象也是存在的

      let hd = Object.create(null, {
       name: {
         value: 111
       }
      });
    
  2. 对象方法和原型方法的使用优先级。

    即使给_proto_给原型上添加相同方法。依然会先优先使用自己自身的方法。

  3. 引入prototye

    注意:prototype是函数才有的属性,而__proto__是每个对象都有的属性

    function User () { };
    User.prototype.show = function () {   // 1.作为构造函数
      console.log('User.prototype.show');
    }
    let userObj = new User();
    console.log(userObj.__proto__.show === User.prototype.show); // true
    
    User.__proto__.hide = function () {   // 2. 作为普通对象
      console.log('User.__proto__.hide');
    };
    
    console.dir(User);
    
  4. 原型关系

    上面User的prototype和__proto__的原型指得是Object的prototype

    Object.prototype.ObjFunc = function () {
        console.log('这里是Object才有的方法');
    }
    
    console.log(Object.prototype === User.__proto__.__proto__,  
                Object.prototype === User.prototype.__proto__) // true
    

​ 而Object.prototype.__proto__指的是null,因为Object.prototype已经到顶了。

  1. 自定义对象的原型设置

    let son = {
        name: '儿子'
    }
    
    let father = {
        name: '父亲',
        getName () {
            console.log('名字:',this.name);
        }
    }
    
    // 原型设置, 和Object.create()类似。与之相匹配的是getPrototypeOf()
    Object.setPrototypeOf(son, father); 
    son.getName();
    
  2. prototye中constructor的引用

    function User (name) { 
        this.name = name;
    };
    
    // 在原型上添加一个show方法
    User.prototype.show = function () {
        console.log('User.prototype.show:', this.name);
    }
    
    // 但是,如果将上面的改写成下面的,后面new的时候是会报错的,因此要加上constructor
    User.prototype = {
        // constructor: User,   
        show () {
            console.log('User.prototype.show:', this.name);
        }
    }
    
    let lisi = new User.prototype.constructor('lisi');
    lisi.show();
    
  3. 给我一个对象还你一个世界

    function User (name) { 
    	this.name = name;
    };
    User.prototype.show = function () {
    	console.log('User.prototype.show:', this.name);
    }
    
    function createNewObj (obj, arg) {
        let prototype = Object.getPrototypeOf(obj).constructor; // 注意:obj.__proto__不是一个标准的方法
        return new prototype(arg);
    }
    
    let lisi = new User('李四');
    let newObj = createNewObj(lisi, '王五');  // 关键代码,传递对象过去获取到新的对象
    newObj.show(); // User.prototype.show: 王五
    
  4. 原型链检测instanceof

    注意:instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

  5. Object.isPrototypeOf() 原型检测

  6. in与hasOwnProperty

    in会沿着原型链一直往上对象的属性,而hasOwnProperty只会在当前对象找对应属性。

  7. 使用apply或者call来借用其他原型上的方法

    <body>
        <button class="hasCls"></button>
        <button class="hasCls"></button>
        <button></button>
    </body>
    
    <script>
        let buttons = document.querySelectorAll('button');
        let hasClsButton = ([] || Array.prototype).filter.call(buttons, item => {
        	return item.hasAttribute('class');
        });
    
        console.log(buttons, hasClsButton);
    </script> 
    
  8. 合理的构造函数方法声明

    function User (name) { 
        this.name = name;
        this.show = function () {
        	console.log(this.name);
        }
    };
    
    // 没有必要花费更多的内存,把show方法定义到每个实例上
    // 可以把show()方法定义在原型链上.
    // 如果方法多的话,可以定义到 User.prototype = {}里面
    User.prototype.show = function () {
        console.log(this.name);
    }
    
    let zhangsan = new User('张三');
    let lisi = new User('李四');
    console.log(zhangsan, lisi);
    

13.__proto__原来是属性访问器

let test = {};
test.__proto__ = {
    show () {
    	console.log('方法设置在原型对象上');
    }
}
console.log(test.show());

test.__proto__ = '覆盖__proto__';  // 没有成功覆盖。原因是__proto__的set对其做了限制. 可以使用以下方法实现:
let test = Object.create(null);
test.__proto__ = '覆盖__proto__';
console.log(test.__proto__);
  1. 改变构造函数原来不是继承

    有什么办法让Admin和Member继承User的role()方法,第一种实现方式是让Admin和Member的直接原型指向User。

    function User () {};
    let user = new User();
    User.prototype.role = function () {
        console.log('这里是User的role方法');
    }
    User.prototype.name = function () {
        console.log('这里是User的name方法');
    }
    
    function Admin () {};
    Admin.prototype = User.prototype;
    Admin.prototype.role = function () {
        console.log('这里是Admin的role方法');
    }
    let admin = new Admin();
    
    console.log(user.role());  // user的role方法被覆盖。输出:这里是Admin的role方法  
    

    上面这一种实现方式是有问题的,role方法会被覆盖。应该变换成下面这种实现方式。

    function User () {};
    User.prototype.role = function () {
    	console.log('这里是User的role方法');
    }
    User.prototype.name = function () {
    	console.log('这里是User的name方法');
    }
    
    function Admin () {};
    Admin.prototype.__proto__ = User.prototype;
    Admin.prototype.role = function () {
    	console.log('这里是Admin的role方法');
    }
    let admin = new Admin();
    
     // 既可以实现继承,又可以在自身原型上修改role()方法
    console.log(admin.role(), admin.name()); // 这里是Admin的role方法; 这里是User的name方法
        
    
  1. 原型链继承
function User () {};

function Admin () {};
let admin = new Admin();  // 因为admin实例是在下面role方法定义之前new出来的,所以是没有role方法的

// Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
// Admin.prototype = Object.create(User.prototype);
// 上面那样写,最后是会报错的,下面才是正解
Admin.prototype.__proto__ = User.prototype;

// 这时候即使给Admin的原型上挂上role方法,admin对象也是取不到的,因为它相当于在User.prototype上新增role方法
Admin.prototype.role = function() { 
    console.log('role');
};

let admin1 = new Admin();

admin.role();  // 会报错,因为没有role方法
  1. 禁止contructor被遍历

下面这种情况,如果只是简单的Object.create,他是没有构造函数的。因此需要手动给contructor赋值。赋值方式有两种,一种是Admin.prototype.constructor = User;,不过,构造函数就是可枚举的了。因此使用Object.defineProperties将其变成不可枚举的。

function User () {};

function Admin () {};
let admin = new Admin();  // 因为admin实例是在下面role方法定义之前new出来的,所以是没有role方法的

// Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
Admin.prototype = Object.create(User.prototype);

// 但是这时候,是没有constructor的,需要手动赋值
// 但是这种赋值方式会有问题,将其变成可枚举的了
// Admin.prototype.constructor = User;

Object.defineProperties(Admin.prototype, {
    'constructor': {
        value: User,
        enumerable: false
    }
});

let admin1 = new Admin();

// 如果按照Admin.prototype.constructor = User;来写的话,会打印出constructor,将其变成可枚举的。
// 因此应该使用Object.defineProperties的形式
for (let item in admin1) {
  console.log(item);
}
  1. 面向对象的多态

    function User () {};
    User.prototype.showMsg = function () {
    	return console.log(this.msg());
    }
    
    function Admin () {};
    Admin.prototype = Object.create(User.prototype);
    Admin.prototype.msg = function () {
    	return '这里是Admin的msg方法';
    }
    
    function Members () {};
    Members.prototype = Object.create(User.prototype);
    Members.prototype.msg = function () {
    	return '这里是Members的msg方法'; 
    }
    
    for (let item of [new Admin(), new Members()]) {
    	item.showMsg();
    }
    
  2. 使用对象工厂派生对象并实现继承

        function User (name, age) {
          this.name = name;
          this.age = age;
        };
    
        User.prototype.showMsg = function () {
          console.log(`名字:${this.name},年龄:${this.age}`);
        }
    
        function Admin (name, age) {
          let user = Object.create(User.prototype);
          user.showInfo = function () {
            console.log('这里是Admin方法里的showInfo方法');
          };
          User.call(user, name, age);
          return user;
        };
    
        let admin = Admin('余晖', '21岁');
        admin.showMsg();
        admin.showInfo();
    

19 . super

super关键字用于访问和调用一个对象的父对象上的函数。

const person = {
	name:'jack'
}

const man = {
	sayName(){
		return super.name;
	}
}

Object.setPrototypeOf( man, person );

let n = man.sayName();

console.log( n )	//jack

学习视频

第十一章 JS的原型和继承包你学会,让javascript功力再上一层,赶紧上车吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值