- A+
所属分类:Web前端
<script> 'use strict'; // let arr = [1, 2, 3]; // let res = arr.concat(5, 6); // console.log(res); // 原型链 // __proto__ 上一级原型 Array(0) // __proto__ 上上一级原型 Object let obj1 = {}; console.log(obj1);// __proto__ 上一级原型 Object // 获取原型 let proto1 = Object.getPrototypeOf(obj1); console.log(obj1); let obj2 = { name: 'cyy' }; let proto2 = Object.getPrototypeOf(obj2); console.log(proto1 == proto2); </script>
没有原型的对象也是存在的:
<script> 'use strict'; //Object.create 指定原型和属性 // 完全的数据字典对象 let obj = Object.create(null, { name: { value: 'cyy' } }); console.log(obj); //hasOwnProperty是原型的方法,由于obj没有原型,因为无法使用该方法 console.log(obj.hasOwnProperty('name')); </script>
原型方法与对象方法的优先级:
<script> 'use strict'; //自己有就执行自己的,自己没有就执行原型的,原型也没有就没法执行 // let obj = { // show() { // console.log('obj.show'); // } // } // obj.__proto__.show = function () { // console.log('obj.__proto__.show'); // } // obj.show(); let obj = {} obj.__proto__.show = function () { console.log('obj.__proto__.show'); } obj.show(); </script>
函数拥有多个长辈:
1.每个对象都具有一个名为__proto__的属性;
2.每个构造函数(构造函数标准为大写开头,如Function(),Object()等等JS中自带的构造函数,以及自己创建的)都具有一个名为prototype的方法(注意:既然是方法,那么就是一个对象(JS中函数同样是对象),所以prototype同样带有__proto__属性);
3.每个对象的__proto__属性指向自身构造函数的prototype;
<script> 'use strict'; // function user() { } // // 打印结果 // console.log(user); // // 打印详细结构 // console.dir(user); // prototype和__proto__都是原型,但是使用场景不一样 // prototype是构造函数的,__proto__是对象的 function User() { } let obj = new User(); console.log(obj.__proto__ == User.prototype); </script>
原型关系详解与属性继承实例:
<script> 'use strict'; // 系统常见构造函数 Number String Function Object let obj = new Object(); obj.name = 'cyy'; // console.dir(obj);// 对象有__proto__属性,没有prototype属性 Object.prototype.show = function () { console.log('Object-prototype-show'); } // console.dir(Object);// 系统构造函数Object有__proto__属性和prototype属性 // Object.prototype的原型为null // console.dir(Object.prototype.__proto__); function User() { } //构造函数User有__proto__属性和prototype属性 console.dir(User); console.log(User.__proto__.__proto__ == User.prototype.__proto__); let u = new User(); u.show(); User.show(); </script>
系统构造函数的原型体现:
<script> 'use strict'; // let obj = {}; // console.log(obj.__proto__ == Object.prototype); let arr = []; console.log(arr.__proto__ == Array.prototype); Array.prototype.show = function () { console.log('show'); } arr.show(); // let str = '111'; // console.log(str.__proto__ == String.prototype); // let bool = true; // console.log(bool.__proto__ == Boolean.prototype); // let reg = /a/i; //new RegExp // console.log(reg.__proto__ == RegExp.prototype); </script>
自定义对象的原型设置:
<script> 'use strict'; let child = { name: 'child' }; let parent = { name: 'parent', show() { console.log('show:' + this.name); } }; console.log(child.__proto__ == Object.prototype); // 设置原型 Object.setPrototypeOf(child, parent); child.show(); // 获取原型 console.log(Object.getPrototypeOf(child)); </script>
原型中的constructor引用:
<script> function User(name) { this.name = name; } console.dir(User); // prototype是对象,对象的原型用__proto__获取 // 构造函数通过prototype来找原型 console.log(User.prototype.__proto__ == Object.prototype); console.log(User.__proto__.__proto__ == Object.prototype); // 原型通过constructor来找构造函数 console.log(User.prototype.constructor == User); let cyy = new User.prototype.constructor('cyy'); console.log(cyy); // __proto__只服务于对象自己本身 // 在prototype中加功能 // User.prototype.show = function() { // console.log(this.name); // } // cyy.show(); // 同时添加多个功能 User.prototype = { constructor: User, show1() { console.log('show1'); }, show2() { console.log('show2'); } }; let cyy2 = new User.prototype.constructor('cyy'); console.log(cyy2); cyy2.show1(); cyy2.show2(); </script>
给我一个对象还你一个世界:
<script> function User(name) { this.name = name; // this.show = function() { // console.log(this.name); // } } let cyy = new User('cyy'); // console.log(cyy); User.prototype = { constructor: User, show() { console.log(this.name); } }; function createByObject(obj, ...args) { const constructor = Object.getPrototypeOf(obj).constructor; // 获取指定对象的构造函数 // console.log(constructor == User); return new constructor(...args); } let cyy2 = createByObject(cyy, 'cyy的子对象'); console.log(cyy2); cyy2.show(); </script>
总结一下原型链:
<script> // let arr = []; // // arr是对象,对象只有__proto__属性 // console.log(arr.__proto__ == Array.prototype); // console.log(arr.__proto__.__proto__ == Object.prototype); // console.log(Object.prototype.__proto__); //null let a = { name: 'a' }; let c = { name: 'c' }; let b = { name: 'b', show() { console.log(this.name) } }; Object.setPrototypeOf(a, b); //a的原型设置为b console.log(a); a.show(); Object.setPrototypeOf(c, b); //a的原型设置为b console.log(c); c.show(); </script>
原型链检测之instanceof:
<script> function A() {} function B() {} function C() {} // 这里顺序很重要,先修改A的原型,再实例化A let c = new C(); B.prototype = c; let b = new B(); A.prototype = b; let a = new A(); // 检测a的原型链上是否有A的prototype console.log(a instanceof A); console.log(a instanceof Object); console.log(a instanceof B); console.log(a instanceof C); console.log(b instanceof C); </script>
Object.isPortotypeOf 原型检测:
<script> let a = {}; let b = {}; let c = {}; Object.setPrototypeOf(b, c); console.log(b.isPrototypeOf(a)); //b是否在a的原型链上 console.log(b.__proto__ == Object.prototype); console.log(b.__proto__.isPrototypeOf(a)); Object.setPrototypeOf(a, b); console.log(b.isPrototypeOf(a)); console.log(c.isPrototypeOf(a)); console.log(c.isPrototypeOf(b)); </script>
in与hasOwnProperty的属性差异:
let a = { name: 'a' }; let b = { age: 18 }; console.log('name' in a); //name属性是否在a对象上,或者在a的原型链上 console.log('web' in a); Object.prototype.web = 'url'; console.log('web' in a); console.log(a.hasOwnProperty('name')); // 检测a对象是否含有name属性,不会去检测原型链 Object.setPrototypeOf(a, b); console.log('age' in a); console.log(a.hasOwnProperty('age')); for (const key in a) { // console.log(key); if (a.hasOwnProperty(key)) { console.log(key); } }
使用call或者apply借用原型链:
<script> // let obj = { // data: [11, 44, 2, 77, 2] // }; // Object.setPrototypeOf(obj, { // max() { // return this.data.sort((a, b) => b - a)[0]; // 从大到小排序之后的数组,最大值在第一位 // } // }); // console.log(obj.max()); // let lessonObj = { // lessons: { // html: 3, // css: 58, // js: 88 // }, // //getter // get data() { // return Object.values(this.lessons); // } // }; // let res = obj.max.apply(lessonObj); // 借用其他对象原型链中的方法 // console.log(res); //没有this参数的情况 let obj = { data: [11, 44, 2, 77, 2] }; Object.setPrototypeOf(obj, { max(data) { return data.sort((a, b) => b - a)[0]; // 从大到小排序之后的数组,最大值在第一位 } }); console.log(obj.max(obj.data)); let lessonObj = { lessons: { html: 3, css: 58, js: 88 } }; let res = obj.max.call(null, Object.values(lessonObj.lessons)); //没有用到this,第一个参数可以设置为null console.log(res); </script>
优化方法借用:
<script> // console.log(Math.max(22, 55, 33)); // let obj = { // data: [11, 44, 2, 77, 2] // }; // console.log(Math.max.apply(null, obj.data)); // let lessonObj = { // lessons: { // html: 3, // css: 58, // js: 88 // } // }; // console.log(Math.max.apply(null, Object.values(lessonObj.lessons))); // 使用展开语法 let arr = [22, 55, 33]; console.log(Math.max(...arr)); let obj = { data: [11, 44, 2, 77, 2] }; console.log(Math.max.call(null, ...obj.data)); let lessonObj = { lessons: { html: 3, css: 58, js: 88 } }; console.log(Math.max.call(null, ...Object.values(lessonObj.lessons))); </script>
DOM节点借用Array原型方法:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>demo</title> </head> <body> <button message="cyy1" class="red">cyy1</button> <button message="cyy2">cyy2</button> <script> //array的过滤操作 let arr = [1, 2, 3, 4, 5]; console.log(arr.filter(item => item > 3)); console.dir(arr.__proto__.filter); console.dir(Array.prototype.filter); const btns = document.querySelectorAll('button'); // let res = Array.prototype.filter.call(btns, btn => { // // console.log(btn); // return btn.hasAttribute('class'); // }); let res = [].filter.call(btns, btn => { // console.log(btn); return btn.hasAttribute('class'); }); console.log(res); console.log(res[0].innerHTML); </script> </body> </html>
合理的构造函数方法声明:
<script> // function User(name) { // this.name = name; // this.show = function() { // console.log(this.name); // } // } // let cyy1 = new User('cyy1'); // let cyy2 = new User('cyy2'); // console.dir(cyy1); // console.dir(cyy2); //这里的show方法写在构造函数里面,存在内存浪费,可以组合使用构造函数方法和原型方法 // function User(name) { // this.name = name; // } // User.prototype.show = function() { // console.log(this.name); // } // let cyy1 = new User('cyy1'); // let cyy2 = new User('cyy2'); // console.dir(cyy1); // console.dir(cyy2); // 多个方法 function User(name) { this.name = name; } User.prototype = { constructor: User, show() { console.log(this.name); } } let cyy1 = new User('cyy1'); let cyy2 = new User('cyy2'); console.dir(cyy1); console.dir(cyy2); </script>
this和原型没有关系的:
this与原型无关,始终指向调用原型的对象
始终指向函数运行的上下文
不要滥用原型:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>demo</title> </head> <body> <button onclick="this.hide()">btn</button> <script> // 不建议在系统的原型中追加方法 Object.prototype.hide = function() { // console.log('hide'); this.style.display = 'none'; //this指向被点击的button } </script> </body> </html>
Object.create与__proto__:
<script> // 单个对象修改原型的方法 let User = { show() { console.log(this.name); } } // 1、通过Object.create创建对象并指定原型 // 缺点:只能定义原型,不能获取 // let cyy = Object.create(User, { // name: { // value: 'cyy' // } // }); // cyy.show(); // console.log(cyy); // let cyy2 = Object.create(User); // cyy2.name = 'cyy2'; // cyy2.show(); // 2、使用__proto__,可以设置原型,也可以获取原型 let cyy = { name: 'cyy' }; cyy.__proto__ = User; cyy.show(); console.log(cyy.__proto__); </script>
使用setPrototypeOf替代__proto__:
<script> // 单个对象修改原型的方法 let User = { show() { console.log(this.name); } } // __proto__是非标准操作 // setPrototypeOf()是标准操作 // 推荐使用Object.setPrototypeOf定义原型,使用Object.getPrototypeOf获取原型 let cyy = { name: 'cyy' }; Object.setPrototypeOf(cyy, User); cyy.show(); console.dir(cyy); console.log(Object.getPrototypeOf(cyy)); </script>
__proto__原来是属性访问器:
<script> // __proto__ getter setter let User = { name: 'user' } User.__proto__ = { show() { console.log(this.name); } } User.show(); console.log(User.__proto__); User.__proto__ = 99; // 智能判断,如果是对象,则修改原型;否则不修改 console.log(User.__proto__); let user = { action: {}, get proto() { return this.action; }, set proto(obj) { if (obj instanceof Object) { this.action = obj; } } } // let obj = 99; // user.proto = obj; // console.log(user.proto); let obj = { name: 'obj' }; user.proto = obj; console.log(user.proto); let obj2 = {}; console.dir(obj2); // 如何让对象设置__proto__属性为非对象? // 不继承Object即可 let obj3 = Object.create(null); obj3.__proto__ = 'obj3'; console.log(obj3); </script>
改变构造函数原型并不是继承:
<script> // 原型的继承,而不是改变构造函数的原型 // function User() { // this.name = function() { // console.log('name method'); // } // } // let cyy = new User('cyy'); // console.dir(cyy); function User() {} User.prototype.name = function() { console.log('User name'); } let cyy = new User('cyy'); console.dir(cyy); // 改变构造函数的原型 function Admin() {} Admin.prototype = User.prototype; // 这是赋值,而不是继承。改变Admin的原型的同时,也改变了User的原型 Admin.prototype.role = function() { console.log('admin role'); } function Member() {} Member.prototype = User.prototype; Member.prototype.role = function() { console.log('member role'); } let m = new Member(); m.name(); let a = new Admin(); a.name(); a.role(); </script>
继承是原型的继承:
<script> // let f = {}; // console.dir(f.__proto__); // console.log(Object.getPrototypeOf(f)); //查看原型 // 1、这种设置方式,对于顺序没有要求 // 实现原型的继承,保留本身的方法和属性,不会被覆盖和互相影响 // function User() {} // User.prototype.name = function() { // console.log('User name'); // } // // 改变构造函数的原型 // function Admin() {} // // Admin.prototype.__proto__指向Object.prototype,就是指向null // Admin.prototype.__proto__ = User.prototype; // 这是原型的继承 // Admin.prototype.role = function() { // console.log('admin role'); // } // function Member() {} // // Member.prototype.__proto__指向Object.prototype,就是指向null // Member.prototype.__proto__ = User.prototype; // 这是原型的继承 // Member.prototype.role = function() { // console.log('member role'); // } // let a = new Admin(); // a.role(); // let m = new Member(); // m.role(); // 2、这种设置方式,对顺序有要求 function User() {} User.prototype.name = function() { console.log('User name'); } // 改变构造函数的原型 function Admin() {} Admin.prototype.role = function() { //这个role方法在原来的Admin原型对象上,修改后就没有了 console.log('admin role'); } Admin.prototype = Object.create(User.prototype); function Member() {} Member.prototype.role = function() { console.log('member role'); } Member.prototype = Object.create(User.prototype); let a = new Admin(); a.role(); let m = new Member(); m.role(); </script>
继承对新增对象的影响:
<script> // 1、这种设置方式,对于顺序没有要求 // function User() {} // User.prototype.name = function() { // console.log('User name'); // } // // 改变构造函数的原型 // function Admin() {} // let a = new Admin(); // // 改变原来原型对象的原型,就是Object.prototype的原型 // Admin.prototype.__proto__ = User.prototype; // Admin.prototype.role = function() { // console.log('admin role'); // } // a.role(); // 2、这种设置方式,对顺序有要求 function User() {} User.prototype.name = function() { console.log('User name'); } // 先实例化,再改变构造函数的原型;此时对象不具有新的原型对象的方法 function Admin() {} let a = new Admin(); Admin.prototype = Object.create(User.prototype); Admin.prototype.role = function() { //这个role方法在原来的Admin原型对象上,修改后就没有了 console.log('admin role'); } a.role(); </script>
继承对constructor属性的影响:
<script> // function User() {} // let obj1 = new User(); // console.log(obj1.__proto__.constructor == User); // let obj2 = new obj1.__proto__.constructor; // console.log(obj2); function User() {} User.prototype.name = function() { console.log('User name'); } // 先实例化,再改变构造函数的原型;此时对象不具有新的原型对象的方法 function Admin() {} Admin.prototype = Object.create(User.prototype); //这种方式指定原型,没有constructor Admin.prototype.constructor = Admin; //手动指定constructor Admin.prototype.role = function() { //这个role方法在原来的Admin原型对象上,修改后就没有了 console.log('admin role'); } let a = new Admin(); console.log(a.__proto__); let b = new a.__proto__.constructor; console.log(b); </script>
禁止constructor被遍历:
<script> function User() {} User.prototype.name = function() { console.log('User name'); } function Admin() {} Admin.prototype = Object.create(User.prototype); //这种方式指定原型,没有constructor Object.defineProperty(Admin.prototype, 'constructor', { value: Admin, enumerable: false, //不允许遍历 }); console.log(Object.getOwnPropertyDescriptors(Admin.prototype)); Admin.prototype.role = function() { console.log('admin role'); } let a = new Admin(); for (const key in a) { console.log(key); } </script>
方法重写与父级属性访问:
<script> function User() {} User.prototype.name = function() { console.log('User name'); } User.prototype.age = 18; function Admin() {} Admin.prototype = Object.create(User.prototype); //这种方式指定原型,没有constructor Admin.prototype.constructor = Admin; Admin.prototype.role = function() { console.log('admin role'); } // 重写父类中的方法,并使用父类中的属性 Admin.prototype.name = function() { console.log(User.prototype.age + ' admin name'); } let a = new Admin(); a.name(); </script>
面向对象的多态:
<script> function User() {} User.prototype.show = function() { this.role(); //role方法在每个子对象中实现 } function Admin() {} Admin.prototype = Object.create(User.prototype); Admin.prototype.role = function() { console.log('admin role'); } function Member() {} Member.prototype = Object.create(User.prototype); Member.prototype.role = function() { console.log('member role'); } for (const obj of[new Admin, new Member]) { obj.show(); } </script>
使用父类构造函数初始属性:
<script> function User(name, age) { this.name = name; this.age = age; } User.prototype.show = function() { console.log(this.name + this.age); } function Admin(...args) { User.apply(this, args); } Admin.prototype = Object.create(User.prototype); function Member(name, age) { User.call(this, name, age); } Member.prototype = Object.create(User.prototype); let a = new Admin('admin', 18); let b = new Member('member', 20); a.show(); b.show(); </script>
使用原型工厂封装继承:
<script> function extend(sub, sup) { sub.prototype = Object.create(sup.prototype); Object.defineProperty(sub.prototype, 'constructor', { value: sub, enumerable: false }); }; function User(name, age) { this.name = name; this.age = age; } User.prototype.show = function() { console.log(this.name + this.age); } function Admin(...args) { User.apply(this, args); } extend(Admin, User); let admin = new Admin('cyy', 18); admin.show(); function Member(name, age) { User.call(this, name, age); } extend(Member, User); let member = new Member('cyy2', 22); member.show(); </script>
对象工厂派生对象并实现继承:
<script> function User(name, age) { this.name = name; this.age = age; } User.prototype.show = function() { console.log(this.name + this.age); } function admin(name, age) { let instance = Object.create(User.prototype); User.call(instance, name, age); instance.info = function() { console.log('admin--info'); } return instance; } let cyy1 = admin('cyy1', 11); cyy1.show(); cyy1.info(); </script>
多继承造成的困扰: