原型初步认识 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let arr = ["lisi" ];console .log(arr);let obj = {};console .log(obj);let obj2 = {};console .log(Object .getPrototypeOf(obj));console .log(Object .getPrototypeOf(obj2) === Object .getPrototypeOf(obj));
没有原型的对象也是存在的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let div = document .quer;let hd = { name: "李四" , }; console .log(hd);console .log(hd.__proto__);let newhd = Object .create(null , { name: { value: "李四" , }, }); console .log(newhd);console .log(newhd.__proto__);
原型方法与对象方法的优先级 如果对象本身的方法名和原型上的方法名相同,则会使用对象上的方法名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let obj = { name: "666" , }; console .log(obj.valueOf()); console .log(obj);let obj2 = {};let obj3 = { show ( ) { console .log("obj3 from function" ); }, }; obj3.__proto__.show = function ( ) { console .log("__proto__方法" ); }; obj3.show();
函数拥有多个长辈 函数有两个原型,分别是__proto__
和prototype
,其中__proto__
可以理解为函数自己的原型,prototype
是这个函数作为构造 函数时实例化函数的原型__proto__
等于函数的prototype
。
1 2 3 4 5 6 function User ( ) {}console .dir(User);
会发现函数有__proto__
和prototype
两个原型,__proto__
可以理解为这个函数自己本身的原型,prototype 是另一个方法的原型,两个原型各司其责。
1 2 3 let use = new User();console .dir(use);
通过打印 use 发现 use 也有一个 proto 的原型,点开查看后可以发现和上面 User 的 prototype 里面的内容是一样的。于是总结出:当一个变量 new 一个构造函数后,这个构造函数会把自己的 prototype 给到这个变量,这个变量吧构造函数的 prototype 当做自己的 proto 原型来使用。
为了验证上面的说法,可以对比 use 的 proto 和 User 的 prototype 是否相同
1 console .log(use.__proto__ == User.prototype);
打印显示 true,说明它们两个是一样的。
接着往 User 构造函数的 prototype 原型上添加 show 方法
1 2 3 User.prototype.show = function ( ) { console .log("来自User的prototype原型" ); };
结果是可以运行。因此如果调用方法时,会先找自己有没有这个方法,没有的话会往原型链上找,原型链上有的话就调用原型链上的方法。
接着往构造函数的 proto 原型上添加 getname 方法
1 2 3 User.__proto__.getname = function ( ) { console .log("来自User的__proto__原型" ); };
控制台会报错,所以构造函数只会把自己的 prototype 原型给变量,不会吧 proto 给变量
原型关系详解和属性的继承实例 在 js 中有很多东西都可以 new 出来,比如 new String() new Number new Object 它们都可以被作为构造函数来使用,现在我们先看一下 Object 这个构造函数的原型
1 2 3 4 5 6 let aaa = new Object ();console .dir(aaa);
打印出 aaa 的结构,通过分析发现 aaa 也有 proto 和 prototype 两个原型,我们已经知道 proto 原型是自己独立使用时的原型,prototype 原型被作为构造函数时会用到
我们往 Object 的 prototype 原型中添加一个 show 方法
1 2 3 Object .prototype.show = function ( ) { console .log("来自Object的prototype原型里面的show方法" ); };
使用 aaa 调用 Object 原型上的 show 方法
既然 Object 作为一个类型存在,直接声明一个 function 来看一下它的原型是什么样子的
1 2 3 4 function User ( ) {}console .dir(User);console .log(User.__proto__.__proto__ === Object .prototype); console .log(User.__proto__.__proto__ === User.prototype.__proto__);
通过分析发现 User 的 proto 原型和 prototype 原型都有一个 proto 父级 并且这个proto 父级都有 show 方法,在反观 Object 的 prototype 原型并没有 proto 父级
我们用 User.show()看能否执行 29 行的代码
结果正常打印,那么 User 作为构造函数时,构造函数能够也可以用 show 方法吗?
1 2 3 let newuse = new User();newuse.show(); console .dir(newuse);
结果也是可以的,打印发现 show 方法存在于 newuse 的 proto 原型的父级 proto 原型上,通过层层对比结果如下
1 2 3 4 console .log(newuse.__proto__.__proto__ === User.prototype.__proto__); console .log(User.prototype.__proto__ === User.__proto__.__proto__); console .log(User.__proto__.__proto__ === Object .prototype); console .log(Object .prototype === newuse.__proto__.__proto__);
总结: newuse 的原型来自 User 的 prototype 原型,User 的 prototype 原型里面包含有proto 父级,这个父级来自 Object 的 prototype,Object 的 prototype 原型中存在 show 方法 流程图大致如下:
newuse.proto ↓
User.prototype.proto ↓
Object.prototype
系统构造函数的体现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let number = 55 ; console .log(number.__proto__ == Number .prototype);let obj = {}; console .log(obj.__proto__ == Object .prototype);let arr = [];console .log(arr.__proto__ == Array .prototype);let str = "" ;console .log(str.__proto__ == String .prototype);let bol = true ;console .log(bol.__proto__ == Boolean .prototype);
自定义对象的原型设置
设置对象原型 Object.setPrototypeOf()
读取对象原型 Object.getPrototypeOf()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let obj = { name: "lisi" , }; let objparent = { name: "张三" , show ( ) { console .log(this .name); }, }; Object .setPrototypeOf(obj, objparent);console .dir(obj);obj.show(); console .log(Object .getPrototypeOf(obj));
原型中的 constructor 引用 函数的 prototype 原型里面的 constructor 等于这个函数本身
User.prorotype.constructor === User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function User (name ) { this .name = name; } console .dir(User);console .log(User.prototype.constructor);console .log(User.prototype.constructor == User); let use = new User.prototype.constructor("李四" );console .log(use.name);User.prototype = { constructor : User , show ( ) { console .log("this.show" ); }, getname ( ) { console .log("this.getname" ); }, }; User.prototype.show();
给我一个对象还你一个世界 构造函数实例化出来的对象的 proto 的 constructor 等于构造函数
1 2 let time = new Date ();console .log(time.__proto__.constructor === Date );
下面例子使用对象创建一个新的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function User (...name ) { this .name = [...name]; this .show = function ( ) { console .log(this .name); }; } let hd = new User("lisi" , "wangwu" );function createobj (obj, ...arg ) { const cont = Object .getPrototypeOf(obj).constructor; return new cont(...arg); } let zs = createobj(hd, "张三" , "李四" );zs.show(); hd.show();
原型链总结
Object 的 prototype 原型就是 User 函数的 __proto__ 原型的父级
构造函数的 __proto__ 等于构造函数的 prototype
构造函数的 __proto__ 的父级等于 Object 的 prototype
1 2 3 4 5 6 7 8 9 10 11 12 13 function User ( ) {}console .dir(User);console .dir(Object );console .log(User.__proto__.__proto__ == Object .prototype);let a = new User();console .dir(a);console .log(a.__proto__ == User.prototype);console .log(a.__proto__.__proto__ == Object .prototype);
原型链检测之 instanceof instanceof 检测一个对象的 proto 是否属于某个对象
1 2 3 let arr = {};console .log(arr instanceof Object ); console .log(arr.__proto__ == Object .prototype);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function A ( ) {}function B ( ) {}function C ( ) {}let c = new C();B.prototype = c; let b = new B();A.prototype = b; let a = new A();console .dir(a);console .dir(C);console .log(a instanceof B); console .log(a instanceof C);
Object.isPrototypeOf 原型检测
Object.isPrototypeOf 检测一个对象原型是否是一个对象的原型长辈
1 2 3 4 5 6 7 let a = {};let b = {};console .log(b.isPrototypeOf(a)); Object .setPrototypeOf(a, b);console .log(b.isPrototypeOf(a));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let c = {};let d = {};let e = {};Object .setPrototypeOf(c, d);Object .setPrototypeOf(d, e);console .log(e.isPrototypeOf(d));console .log(e.isPrototypeOf(c));console .log(d.isPrototypeOf(c));
in 和 hasOwnProperty 的属性检测差异
in 语法检测一个属性是否在这个对象上或者在这个对象的原型链上
hasOwnProperty 值检测一个属性是否在这个对象上,不会检测原型链上的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let a = { name : "李四" };let b = { age : "19" };console .log("age" in a); Object .setPrototypeOf(a, b);console .log("age" in a); console .log(a.hasOwnProperty("age" )); for (const key in a) { if (a.hasOwnProperty(key)) { console .log(key); } console .log("没加" + key); }
使用 call 或者 apply 借用原型链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let user = { data: [1 , 65 , 48 , 5 , 4 , 12 , 4 , 5 , 65 , 9 , 25 ], }; Object .setPrototypeOf(user, { max ( ) { return this .data.sort((a, b ) => b - a)[0 ]; }, }); console .log(user.max()); let ls = { lesson: { js: 95 , css: 85 , html: 100 , }, get data () { return Object .values(this .lesson); }, }; console .log(ls.data); console .log(user.max.call(ls)); console .log(user.max.apply(ls)); console .log(user.max.bind(ls)());
DOM 节点借用 Array 数组方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 let btns = document .querySelectorAll("button" );console .log(btns);console .log(btns.filter);console .dir(Array );document .body.innerHTML = "" ;btns = [].filter.call(btns, (item ) => { return item.hasAttribute("color" ); }); console .log(btns);btns.forEach((item ) => { document .body.appendChild(item); });
合理的构造函数方法使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function User (name ) { this .name = name; this .show = function ( ) { console .log(this .name); }; } let a = new User("李四" );let b = new User("王五" );a.show(); b.show(); console .log(a);console .log(b);function Show (name ) { this .name = name; } Show.prototype = { constructor : Show , getname ( ) { console .log(this .name); }, }; let d = new Show("lisi" );let e = new Show("wangwu" );console .dir(d);console .dir(e);d.getname(); e.getname();
this 和原型没有关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let hd = { name: "李四" , }; let User = { name: "张三" , show ( ) { console .log(this ); console .log(this .name); }, }; Object .setPrototypeOf(hd, User);hd.show();
不要滥用原型
1 <button onclick ="this.hide()" > 按钮</button >
1 2 3 4 5 6 7 8 9 Object .prototype.hide = function ( ) { console .log(this ); this .style.display = "none" ; };
Object.create 与__proto__ Object.create 给变量定义原型,这个方法不能用于获取原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let User = { show ( ) { console .log(this .name); }, }; let use = Object .create(User);use.name = "李四" ; console .log(use);use.show(); let newuse = { name: "王五" , }; newuse.__proto__ = User; console .log(newuse.__proto__);newuse.show();
使用 setPrototypeOf 替代__proto__ Object.setPrototypeOf()设置对象原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let User = { show ( ) { console .log(this .name); }, }; let use = { name: "李四" , }; Object .setPrototypeOf(use, User);use.show(); console .log(Object .getPrototypeOf(use));
__proto__是属性访问器 设置一个对象的__proto__时必须使其等于一个对象,否则不会被赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 let use = { name: "李四" , }; use.__proto__ = { show ( ) { console .log(this .name); }, }; use.__proto__ = { getname ( ) { console .log(this .name); }, }; use.__proto__ = "王五" ; use.getname(); console .log(use);let newuse = { active: { name: "lisi" , }, get proto () { return this .active; }, set proto (obj ) { if (obj instanceof Object ) { this .active = obj; } return true ; }, }; newuse.proto = { age: 18 , height: 188 , }; newuse.proto = "wangwu" ; console .log(newuse.proto);
改变构造函数的原型不是继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function User ( ) {}User.prototype.name = function ( ) { console .log("User methed name" ); }; let use = new User();function Admin ( ) {}Admin.prototype = User.prototype; Admin.prototype.name = function ( ) { console .log("admin methed name" ); }; let admin = new Admin();use.name(); admin.name();
继承是原型的继承
Object.create 是重新创建一个原型,在实例化之后对函数进行 Object.create 操作不会改变实例化变量的原型指向
使用 prototype.__proto__ = User 表示改变构造函数的原型指向,所以尽管在实例化之后改变构造函数的原型指向,实例化出来的变量也会受到影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 function User ( ) {}User.prototype.show = function ( ) { console .log("user-metheds-show" ); }; function Age ( ) {}console .log(Age.prototype.__proto__ == Object .prototype); Age.prototype.__proto__ = User.prototype; console .dir(Age);Age.prototype.ageshow = function ( ) { console .log("Age-metheds-show" ); }; let u = new User();let a = new Age();u.show(); a.show(); a.ageshow(); function Info ( ) {}let i = new Info();Info.prototype.__proto__ = User.prototype; i.show(); console .dir(Info);
Object.create()继承造成的影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function User ( ) {}User.prototype.show = function ( ) { console .log("User show" ); }; function Info ( ) {}Info.prototype = Object .create(User.prototype); console .log(User.prototype); console .log(Info.prototype);
上面代码中 Info 的 prototype 原型中的 constructor 没有了,就是因为使用 Object.create 过程中吧 Info 的 constructor 弄丢了,此时去实例化两个构造函数,分别打印出实例化变量的 constructor,发现打印的结果都是 User
1 2 3 4 5 6 7 let u = new User();let i = new Info();console .log(u.constructor); console .log(i.constructor);
通过手动指定 Info 的 constructor 为 Info
1 2 3 4 Info.prototype.constructor = Info; console .log(i.constructor);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 for (const key in i) { console .log(key); } console .log( JSON .stringify(Object .getOwnPropertyDescriptors(Info.prototype), null , 2 ) ); Object .defineProperty(Info.prototype, "constructor" , { value: Info, enumerable: false , }); for (const key in i) { console .log(key); }
方法重写和父级属性访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function User ( ) {}User.prototype.show = function ( ) { console .log("User-show" ); }; User.prototype.site = function ( ) { return "User-site" ; }; function Info ( ) {}Info.prototype.__proto__ = User.prototype; let i = new Info();i.show(); Info.prototype.show = function ( ) { console .log(User.prototype.site() + "Info-show" ); }; i.show();
面向对象的多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function User ( ) {}User.prototype.show = function ( ) { console .log(this .getName()); }; function student ( ) {}student.prototype.__proto__ = User.prototype; student.prototype.getName = function ( ) { return "学生" ; }; function headmaster ( ) {}headmaster.prototype.__proto__ = User.prototype; headmaster.prototype.getName = function ( ) { return "班主任" ; }; function HeadOfDepartment ( ) {}HeadOfDepartment.prototype.__proto__ = User.prototype; HeadOfDepartment.prototype.getName = function ( ) { return "系主任" ; }; for (const obj of [new student(), new headmaster(), new HeadOfDepartment()]) { obj.show(); }
使用父类构造函数初始属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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); let a = new Admin("李四" , 18 );a.show(); function Info (...args ) { User.apply(this , args); } Info.prototype = Object .create(User.prototype); let i = new Info("张三" , 19 );i.show();
使用原型工厂实现封装继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function User (name, age ) { this .name = name; this .age = age; } User.prototype.show = function ( ) { console .log(`${this .name} ,${this .age} ` ); }; function extend (sub, sup ) { sub.prototype = Object .create(sup.prototype); Object .defineProperty(sub.prototype, "construciton" , { value: sub, enumerable: false , }); } function Mumber (...args ) { User.apply(this , args); } extend(Mumber, User); let mumber = new Mumber("李四" , 18 );mumber.show();
对象工厂派生对象并实现继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function User (name, age ) { this .name = name; this .age = age; } User.prototype.show = function ( ) { console .log(`${this .name} ,${this .age} ` ); }; function admin (...args ) { const insta = Object .create(User.prototype); User.apply(insta, args); insta.siate = function ( ) { console .log("si=te" ); }; return insta; } let a = admin("李四" , 18 );a.show(); a.siate(); console .dir(a);let b = admin("王五" , 30 );b.show(); b.siate; console .log(a.__proto__ === b.__proto__);
使用 mixin 实现多继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 function exted (sub, sup ) { sub.prototype = Object .create(sup.prototype); Object .defineProperty(sub.prototype, "construstion" , { value: sub, enumerable: false , }); } const Address = { getaddress ( ) { console .log("获取地址" ); }, }; const Request = { ajax ( ) { return "接口返回" ; }, }; const Iphone = { __proto__: Request, getiphone ( ) { console .log(super .ajax() + "获取手机号" ); }, }; const GetName = { getname ( ) { console .log(this .name); }, }; function User (name, age ) { this .name = name; this .age = age; } User.prototype.show = function ( ) { console .log(this .name, this .age); }; User.prototype = Object .assign(User.prototype, Address, GetName, Iphone); console .dir(User);function Admin (...args ) { User.apply(this , args); } exted(Admin, User); let admin = new Admin("李四" , 15 );admin.show(); admin.getaddress(); admin.getname(); admin.getiphone();