目录
- 1.原型的初步认识
- 2.没有原型的对象也是存在的
- 3.原型方法与对象方法优先级
- 4.函数拥有多个长辈
- 5.原型关系详情与属性继承实例
- 6.系统构造函数的原型提现
- 7.自定义对象的原型设置
- 8.原型中的constructor引用
- 9.原型链检测之instanceof
- 10.Object.isPrototype原型检测
- 11.in与hasOwnProperty属性检测差异
- 12.使用call和apply借用原型链
- 13.优化方法借用
- 14.DOM节点借用Array原型方法
- 15.合理的构造函数方法声明
- 16.this和原型没有关系
- 17.不要滥用原型
- 18.Object.create()和__proto__
- 19.使用setPrototypeOf替代__proto__
- 20.__proto__原来是属性访问器
- 21.改变构造函数原型并不是继承
- 22.继承是原型的继承
- 23.继承对新增对象的影响
- 24.继承对constructor属性的影响
- 25.禁止constructor被遍历
- 26.方法重写与父级属性访问
- 27.面向对象的多态
- 28.使用父类构造函数初始属性
- 29.使用原型工厂封装继承
- 30对象工厂派生对象并实现继承
- 31.使用mixi实现多继承
1.原型的初步认识
let arr = ['A']
console.log(arr.concat("B"));
-1.控制台显示
_proto_就是他的父亲展开之后里面有concat: ƒ concat(),说明它调用的是父亲的方法,下面还有一层_proto就是他的父亲的父亲,里面也有很多方法。整体就有自己-父亲-父亲的父亲,也就是原型链。
let obj = {}
let obj2 = {}
console.log(Object.getPrototypeOf(obj) == Object.getPrototypeOf(obj2));
Object.getPrototypeOf(obj)获取对象的原型,
-2控制台打印
obj和obj2都一只有一个_proto_也就是他们的父亲,判断obj和obj2是不是一个父亲,判断结果为true,他们两个是一个父亲。
2.没有原型的对象也是存在的
let obj = { name: "小红"}
-1 console.log(obj.hasOwnProperty("name"));
let obj2 = Object.create(null,{
name:{
value:"小明"
}
})
-2 console.log(obj2.hasOwnProperty("name"));
-1控制台打印
打印结果为true,obj对象里面有name属性,hasOwnProperty是它原型里面的方法。
-2控制台打印
因为用Object.create这样创建的对象,是没有原型的,所以找不到hasOwnProperty这个方法。完全数据字面量,没有原型的关系存在。
3.原型方法与对象方法优先级
let obj = {
show() {
console.log('小明');
},
render() {
console.log('obj . render ');
}
}
obj.show()//小明
obj.__proto__.render = function() {
console.log('小红');
}
-1 console.log(obj)
obj.render();//obj . render
-1控制台打印
obj自身里面有render方法,但是下面在它的__proto__中也添加了一个render方法,调用的时候是先在自身找,如果自身有的话,就不会调用父亲的。
4.函数拥有多个长辈
function User() {}
console.dir(User);
控制台打印
可以看到它有两个长辈在同一级__proto__,和prototype,他们的使用场景是不一样的。
function User() {}
let hd = new User()
User.prototype.show = function() {
console.log("小明");
}
hd.show()
console.log(hd);//只有一个长辈__proto__
console.log(User.prototype === hd.__proto__);
创建一个hd构造函数,hd构造函数没有show方法,在User.prototype中添加show方法,hd.show()就能输出了。由此可见hd和User使用的是一个原型。(User.prototype === hd.proto)结果为true。
function User() {}
User.__proto__.view = function() {
console.log("user function view method")
}
User.view() //user function view method
由此可见__proto__服务于函数对象,prototype服务于函数实例化对象(let hd = new User())
5.原型关系详情与属性继承实例
let hd = new Object();
hd.name = "小明"
console.dir(hd);
Object.prototype.show = function() {
console.log("baidu.com");
};
hd.show();
function User() {
};
-1console.dir(User);
-2console.log(User.prototype.__proto__ === User.__proto__.__proto__);
-3console.log(Object.protype.__proto__);
let abc = new User();
abc.show() //baidu.com
User.show() //baidu.com
-1控制台打印
User里面prototype的父级里面有show说明,user.prototype的父级就是Object.prototype
User里面__proto__的父级里面也有show说明,User的prototype和__proto__指向的都是Objece.prototype
-2控制台打印
true
-3控制台打印
null
6.系统构造函数的原型提现
let obj = {}; //new Object
console.log(obj.__proto__ == Object.prototype); // true
Object.prototype.show = function() {
console.log('小明');
}
obj.show()
let arr = []; //new Array
console.log(arr.__proto__ == Array.prototype);// true
let str = '';
console.log(str.__proto__ == String.prototype);// true
let bool = true;
console.log(bool.__proto__ == Boolean.prototype);// true
let reg = /a/i; // new RegExp
console.log(reg.__proto__ == RegExp.prototype);// true
7.自定义对象的原型设置
let obj = { name: "obj"};
let parent = { name: "parent", show() {
console.log('parent method:' + this.name);
}};
console.log(obj.__proto__ = Object.prototype);
Object.setPrototypeOf(obj,parent) //parent是obj的父级
console.log(obj);
obj.show();//parent method:obj 自身没有这个方法去父级寻找
parent.show();//parent method:parent
-1console.log(Object.getPrototypeOf(obj));
Object.setPrototypeOf(obj, prototype)设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null。
obj
要设置其原型的对象。.
prototype
该对象的新原型(一个对象 或 null).
-1控制台打印
获取obj的原型,也就是parent ,之前给它设置作为了obj的原型
8.原型中的constructor引用
function User(name) {
this.name = name;
}
console.dir(User);
console.log(User.prototype.__proto__ == Object.prototype); //true
-1console.log(User.prototype.constructor == User);
let lisi = new User.prototype.constructor('李四')
console.log(lisi);
-1控制台打印
true
所以说User等于User.prototype.constructor
function User(name) {
this.name = name;
}
//想要在原型中添加多个方法
User.prototype = {
show() {
console.log(this.name)
},
show2() {
console.log(this.name)
}
}
-2console.dir(User)
-2控制台打印
这种方式往原型中添加多个方法,创建一个新对象,该变了原型。原来的对象里面有个constructor属性,改变原型之后就没有这个属性了。那我们就不能使用这种方式了let lisi = new User.prototype.constructor(‘李四’),如果我们想要使用这种方式,就需要把constructor配置上。
User.prototype = {
constructor:User,
show() {
console.log(this.name)
},
show2() {
console.log(this.name)
}
}
9.原型链检测之instanceof
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.log(a instanceof B); //true
console.log(a instanceof C); //true
console.log(a instanceof C); //true
console.log(b instanceof A);//false b对象原型链是否有A的构造函数
10.Object.isPrototype原型检测
isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
// a.__proto__ = Object.prototype
let a = { name:'a'};
// b.__proto__ = Object.prototype
let b = {};
let c = {};
Object.setPrototypeOf(a, b)
Object.setPrototypeOf(b,c)
// 一个对象是否在另一个对象的原型链上
// a对象是否在b.__proto__的原型链上
console.log(a);
console.log(b.__proto__.isPrototypeOf(a)); //false
console.log(b.isPrototypeOf(a))//true
11.in与hasOwnProperty属性检测差异
let a = { url: 'baidu.com' };
let b = { name: '小明'};
// Object.prototype.web = 'www'
// console.log('web' in a); //true
Object.setPrototypeOf(a,b); //b设置成a的原型
// a里面没有,但是原型里面有,in不仅检测当前对象,还会检测原型链
console.log('name' in a); //true
// hasOwnProperty单纯检测对象
console.log(a.hasOwnProperty('name'));
for(const key in a) {
console.log(key); // url name
if(a.hasOwnProperty(key)){
console.log(key);//url
}
}
12.使用call和apply借用原型链
1.不传参数写法
let obj = {
data: [1,2,3,34,5,6,7]
}
//max是obj原型里面的方法
Object.setPrototypeOf(obj,{
max() {
return this.data.sort((a,b) => b - a)[0];
}
});
// 取obj里面的最大值
console.log(obj.max());//34
let obj2 = {
lessons:{ js:87, hph:50, node:99,linux:88 },
// getter
//get data() {
//return Object.values(this.lessons)
//}
}
// 取最大值,借用obj里面的max
console.log(obj.max.apply(obj2)); //这样写 max里面的this就是obj2,但是obj2里面没有data属性
必须要在obj2里面添加
get data() {
return Object.values(this.lessons)
}
2.传递参数写法
let obj = {
data: [1,2,3,34,5,6,7]
}
Object.setPrototypeOf(obj,{
max(data) {
return data.sort((a,b) => b - a)[0];
}
});
// 取obj里面的最大值
console.log(obj.max(obj.data));//34
let obj2 = {
lessons:{ js:87, hph:50, node:99,linux:88 },
}
console.log(obj.max.call(null,Object.values(obj2.lessons)));
13.优化方法借用
let obj = {
data: [1,2,3,34,5,6,7]
}
// apply是把data里面的数据展开了一个一个传递进Math.max里面,call是直接把数组作为一个参数传递进去了
console.log(Math.max.apply(null,obj.data));//34
let obj2 = {
lessons:{ js:87, hph:50, node:99,linux:88 },
}
console.log(Math.max.apply(null,Object.values(obj2.lessons)));
14.DOM节点借用Array原型方法
<button class="red">Js</button>
<button>node</button>
let arr = [1, 3, 43];
let res = arr.filter(item => {
return item > 39;
})
console.dir(Array);//Array.prototype里面有filter方法
let btns = document.querySelectorAll('button')
console.log(btns.filter);
btns = Array.prototype.filter.call(btns,item => {
// 找button里面含有class的
return item.hasAttribute('class')
})
console.log(btns);//[button.red]
15.合理的构造函数方法声明
function User(name) {
this.name = name;
this.show = function() {
console.log(this.name)
}
}
let lisi = new User('李四')
let zhangsan = new User('张三')
console.log(lisi);
console.log(zhangsan);
控制台打印
他们两个用的是同一个方法,为了优化可以把方法写在原型里面
User.prototype.show = function() {
console.log(this.name);
}
//如果有多个共用方法
User.prototype = {
//通过原型也要找到构造函数
constructor: User,
show() {
console.log(this.name)
},
demo() {
console.log('demo ...')
}
}
16.this和原型没有关系
let obj = {
name:'张三',
show() {
console.log(this.name)
}
}
console.log(obj.show())
控制台打印
let obj = {
name:'张三',
}
let User = {
name:'李四',
show() {
console.log(this.name)
}
}
Object.setPrototypeOf(obj,User);
console.log(obj.show())
控制台打印
this和原型没太大关系,永远指向调用的属性的对象
17.不要滥用原型
不建议在系统原型上面修改,当遇到重名方法,会优先使用最后定义的,会造成代码不稳定。
<button onclick="this.hide()">node</button>
Object.prototype.hide = function() {
this.style.display = 'none'
}
18.Object.create()和__proto__
let user = {
show() {
return this.name
}
}
//定义对象的原型,不能获取
let obj = Object.create(user,{
name:{
value:'张三'
}
})
//show方法在obj的原型里面
console.log(obj.show())
let user = {
show() {
return this.name
}
}
let hd = { name:'李四'}
hd.__proto__ = user;//改变原型为user
console.log(hd.show());//李四
19.使用setPrototypeOf替代__proto__
let user = {
show() {
return this.name
}
}
let obj = { name:'李四'}
Object.setPrototypeOf(obj,user)
console.log(obj);
console.log(Object.getPrototypeOf(obj));
20.__proto__原来是属性访问器
set\get
let hd = {
action:{},
proto:null,
get proto() {
return this.action
},
set proto(obj) {
if(obj instanceof Object) {
this.action = obj
}
}
};
hd.proto = { view:'111'};
hd.proto = '66'
console.log(hd.proto);//{view: "111"}
给__proto__设置属性字符串是,设置不上的,__proto__里面的set、get会对测试的值进行判断的。
let obj = { name:'张三'};
obj.__proto__ = {
show() {
console.log(this.name);
}
}
obj.__proto__ = 99;
-1console.log(obj.__proto__);
-1控制台打印
如果想给对象__proto__设置属性
let obj = Object.create(null); //不让它有原型
obj.__proto__ = "李四"
console.log(obj)
21.改变构造函数原型并不是继承
原型的继承而不是改变构造函数的继承
function User() {}
User.prototype.name = function() {
console.log('user name method');
}
function Admin() {}
// 改变了构造函数的原型
Admin.prototype = User.prototype
Admin.prototype.role = function() {
console.log('admin a');
}
let b = new Admin();
console.log(b.name);//现在他可以使用User里面的方法
function Member() {}
Member.prototype = User.prototype
Member.prototype.role = function() {
console.log('member a');
}
let a = new Admin();
console.log(a.role());//member a 这样明显是不对的
继承的时候,自己本身的方法应该要保存,这种改变构造函数原型的不叫继承。
22.继承是原型的继承
function User() {}
User.prototype.name = function() {
console.log('user name method');
}
// let hd = new User();
// console.log(hd);
function Admin() {}
// 改变了构造函数的原型
// console.log(Admin.prototype.__proto__ == Object.prototype); true
//方法1
// Admin.prototype.__proto__ = User.prototype
//方法2:创建一个新对象,把User.prototype作为它的原型
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function() {
console.log('admin a');
}
// let a = new Admin();
// console.log(a.role());
function Member() {}
Member.prototype.__proto__ = User.prototype
Member.prototype.role = function() {
console.log('member a');
}
let a = new Admin();
console.log(a.role());//admin a
方法二:
23.继承对新增对象的影响
1.
function User() {}
User.prototype.name = function() {
console.log('user name method');
}
function Admin() {}
let a = new Admin();
// 改变原型对象的父级
Admin.prototype.__proto__ =User.prototype
)
Admin.prototype.role = function() {
console.log('admin.role')
}
a.role();
控制台打印
admin.role
2.
function User() {}
User.prototype.name = function() {
console.log('user name method');
}
function Admin() {}
let a = new Admin();
//新建原型对象
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function() {
console.log('admin.role')
}
a.role();
24.继承对constructor属性的影响
**function User() {}
User.prototype.name = function() {
console.log('user name method');
}
function Admin() {}
Admin.prototype =Object.create(User.prototype);
Admin.prototype.role = function() {
console.log('admin.role')
}
-1console.dir(Admin);
// 此时Admin.prototype并没有constructor,它继承的是User的constructor
// 要加上
Admin.prototype.constructor = Admin**
-1控制台打印
25.禁止constructor被遍历
function User() {}
User.prototype.name = function() {
console.log('user name method');
}
function Admin() {}
Admin.prototype =Object.create(User.prototype);
Admin.prototype.role = function() {
console.log('admin.role')
}
// 查看对象的属性特征
-1console.log(Object.getOwnPropertyDescriptors(Admin.prototype));
//设置constroctor不能被遍历
Object.defineProperty(Admin.prototype,'constructor',{
value:Admin,
enumerable:false
})
// // 禁止constoructor被遍历
let a = new Admin();
console.dir(a);
for( const key in a){
-2console.log(key);
}
-1控制台打印
-2控制台打印
26.方法重写与父级属性访问
function User() {};
User.prototype.show = function (){
console.log('user.name');
}
User.prototype.size = function (){
return '哈哈哈哈'
}
function Admin() {}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.constructor = Admin
Admin.prototype.show = function() {
console.log(User.prototype.size() + 'admin.name');
}
let hd = new Admin();
hd.show() //哈哈哈哈admin.name
27.面向对象的多态
function User() {}
User.prototype.show = function() {
console.log(this.description());
}
function Admin() {}
Admin.prototype = Object.create(User.prototype)
Admin.prototype.description = function() {
return '管理员在此'
}
function Member() {}
Member.prototype = Object.create(User.prototype)
Member.prototype.description = function() {
return '我是会员'
}
for( const obj of [new Admin(),new Member()]){
obj.show();
}
控制台打印
28.使用父类构造函数初始属性
function User(name,age){
this.name = name
this.age = age
}
User.prototype.show = function () {
console.log(this.name,this.age);
}
// function Admin(name,age) {
// User.call(this,name,age);
// }
function Admin(...args){
console.log(args);//["小明", 18]
User.apply(this,args)
}
Admin.prototype = Object.create(User.prototype)
let xm = new Admin('小明',18)
xm.show() //小明 18
29.使用原型工厂封装继承
function User(name,age) {
this.name = name;
this.age = age;
}
User.prototype.show = function() {
console.log(this.name,this.age);
}
// 封装一个方法
function extent(sub,sup) {
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub.prototype,'constructor',{
value:'sub',
enumerable:false
})
}
function Admin(...args) {
User.apply(this,args)
}
extent(Admin,User)
let admin = new Admin('小明',18);
admin.show()//小明 18
function Zoo(...args) {
User.apply(this,args)
}
extent(Zoo,User)
var zoo = new Zoo('狮子',2)//狮子 2
zoo.show()
30对象工厂派生对象并实现继承
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 obj = Object.create(User.prototype)
User.call(obj,name,age)
return obj
}
let lisi = new admin('李四','22')
lisi.show()//李四 22
31.使用mixi实现多继承
Object.assign() 方法
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
function extent(sub, sup) {
sub.prototype = Object.create(sup.prototype)
Object.defineProperty(sub.prototype, 'constructor', {
value: sub,
enumerable: false
})
}
User.prototype.show = function () {
console.log(this.name, this.age);
}
function User(name,age) {
this.name = name;
this.age = age;
}
const PanDa = {
panda() {
console.log('熊猫');
}
}
const Lion = {
lion() {
console.log('狮子');
}
}
function Zoo(name,age) {
User.call(this,name,age)
}
extent(Zoo,User)
// Zoo.prototype.panda = PanDa.panda
Zoo.prototype = Object.assign(Zoo.prototype,PanDa,Lion)
let dog = new Zoo('大黄',5)
dog.show()//大黄5
dog.panda()//熊猫
dog.lion()//狮子