一.原型
原型: 原型也叫作“原型对象”,任何一个js对象都有与之对应的原型
获取对象的原型:对象 _proto_
比如构造函数Tank,new一个实例对象t,求实例对象t的原型:t._proto_得到{constructor...}
对于对象{constructor...}再对它constructor,得到构造函数Tank,而对构造函数Tank求原型对象得到{constructor...},所以我们可以得到以下关系:
对象._proto_==函数.prototype
对象._proto_.constructor==函数
对象._proto_.constructor=函数.prototype.constructor
获取对象原型:函数.prototype
二.原型链
通过对象的__proto__属性一级一级向上查找得到一个有_proto_构成的链条,这个链条就是原型链。
原型链的终点是object的原型
Object是所有对象的顶层构造函数,即所有的对象都可以看做是由Object创建的
instanceof用来判断一个实例对象是否是构造函数的实例
三.set以及set的应用
set的应用:
<script>
//Set中数据不会重复
var s = new Set([1,2,3,4,5,6])
s.add(1); //添加元素到 set,1存在添加不上
//s.delete(3); //从set删除元素,
if( s.has(2) ){ //判断某个元素是否在set中
console.log('2存在');
}
s.forEach((item,index)=>{
console.log(item,index);
})
// s.clear(); //清空 set
console.log(s);
console.log(s.size);
</script>
set去重:
//数组去重
var arr = [1,2,3,4,4,3,2,1];
//写法一
var s = [...new Set(arr)];
console.log(s);
//写法二
var s = Array.from( new Set(arr) )
console.log(s);
Map:
<script>
//Map是种数据结构,类似对象
var m = new Map();
m.set('name','李煜');
//向map结构 添加数据
m.set('age',30);
m.set('sex',false);
console.log( m.get('name') , m.get('age') , m.get('sex') );
//从map结构中获取数据
console.log( m.has('age') );
m.forEach((item,key)=>{
console.log(item,key);
})
console.log(m);
</script>
4.object.defineProperty和Proxy
区别:defineproPerty是对属性的劫持,给对象的某个属性进行监听。proxy是对整个对象的代理,如果需要监听某一个对象的所有属性,需要遍历对象所有属性并对其进行劫持来进行监听。
defineProperty:无法监听对象新增属性,proxy可以;
defineProperty无法监听对象的删除属性,proxy可以;
defineProperty不能监听数组下标改变值的变化,proxy可以且不需要对数组方法进行重载;
defineProperty是循环遍历对象属性的方式来进行监听,自然会比proxy对整个对象进行监听的方式要消耗性能。
var obj = { name:'李煜',age:30, father:{ name:'',age:90, son:{name:'',age:10} } };
//writable 对象的属性是否可修改 ( 修改值 )
//configurable 对象的属性是否可操作 ( 删除 )
//enumerable 对象的属性是否可枚举 ( 遍历 )
Object.defineProperty(obj, 'name', { writable:true })
Object.defineProperty(obj, 'name', { configurable:true })
Object.defineProperty(obj, 'name', { enumerable:true })
Object.defineProperty(obj, 'name', {
// writable:true,
set: function( newValue ){
//在set函数中不能 执行针对该属性的赋值操作,会导致函数的递归调用.
console.log('修改了name属性的值',newValue);
},
get: function(){
//在get函数中不能 执行获取该属性的操作, 会导致函数的递归调用.
console.log('获取了name属性的值');
return '李煜'+'!!!';
},
})
var obj = { name:'李煜',age:30, father:{ name:'',age:90, son:{name:'',age:10} } };
//监听对象的所有属性
//思路:
//1.先用for in 遍历对象, 拿到对象的每个属性
//2.用Object.defineProperty给对象的每个属性设置监听
//作用: 传入一个对象, 对该对象的每个属性进行遍历并且监听.
function listenObj(obj){
for(var key in obj){
if( typeof obj[key] == 'object' ){
//如果某个属性是引用类型, 则递归遍历该属性
listenObj(obj[key]) //递归遍历该属性
}else{ //不是引用类型
Object.defineProperty(obj, key , {
set:()=>{ console.log('修改了属性的值'); },
get:()=>{ console.log('获取了属性的值'); }
})
}
}
}
listenObj(obj)
5.object中的方法属性
注意:当调用对象的某个方法时, 首先在对象自身找该方法,如果自身有该方法则调用成功,如果没有则继续沿着由__proto__构成的原型链查找该方法, 如果一直到原型链的终点都没有找到则报错.
以下为存在于Object中的,是可以找见的方法:
Object.prototype.constructor
Object.prototype.toString()
Object.prototype.toLocaleString()
Object.prototype.hasOwnProperty()
Object.prototype.isPrototypeOf()
判断一个属性是否在对象身上:
hasOwnProperty() 在判断一个属性是否在对象身上时, 不会沿着原型链查找的(只会在对象自身身上查找。)
var obj = { name:'李煜',age:30 }
if( obj.hasOwnProperty('toString') ){
// if( 'toString' in obj ){
console.log('在obj身上');
}else{
console.log('不在obj身上');
}
//输出不在
in 在判断一个属性是否在对象身上时, 会沿着原型链查找
isPrototypeOf() 判断一个对象 是否是某个函数的原型对象
结论: js中任何一个对象都可以看做是 Object这个顶层构造函数的实例对象、
if( Array.prototype.isPrototypeOf( Object ) ){
console.log('是');
}else{
console.log('不是');
}
//打印结果为不是,因为object的原型对象为null
if( Object.prototype.isPrototypeOf( RegExp ) ){
console.log('是');
}else{
console.log('不是');
}
//打印结果为是,因为js中任何一个对象都可以看做是 Object这个顶层构造函数的实例对象