补充
1、Object.prototype中的hasOwnProperty、isPrototypeof
hasOwnProperty //判断一个属性是否是实例自身的属性(不能是原型链上的属性)
isPrototypeof // 判断一个某一个原型对象是否在某个实例的原型链上,类似于instanceof
function Foo(){
this.name = "haha";
}
Foo.prototype.age = 20;
var foo = new Foo();
//判断某一个属性是否是实例本身的属性
console.log(foo.hasOwnProperty("name"),foo.hasOwnProperty("age"));
//true false
//判断某一个原型对象是否在某个实例的原型链上
console.log(Foo.prototype.isPrototypeOf(foo),Object.prototype.isPrototypeOf(foo));
2、Object.assign
// 将一个或多个对象的可枚举(即可遍历到)的属性复制到第一个参数上,返回复制之后的结果。(注:如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。)
var arr = {a:1};
var arr1 = {b:2,c:3};
var arr2 = {d:4,e:5};
var result = Object.assign({},arr,arr1,arr2);
console.log(result);
// 打印arr arr1 arr2拼接到空对象{}的结果
// {a: 1, b: 2, c: 3, d: 4, e: 5}
3、Object.defineProperty
给对象添加某个属性,默认只设置值的情况下
var obj = {};
Object.defineProperty(obj,"a",{
value:10
});
console.log(obj); // {a:10}
obj.a = 20;
console.log(obj.a); // 10,默认不可更改
for(let i in obj){
console.log(i); // 默认不可遍历到,无打印
}
delete obj.a;
console.log(obj.a); // 10,默认不可删除
进行设置权限操作
var obj = {};
Object.defineProperty(obj,"a",{
value:10,
writable:true, // 可写
enumerable:true, // 可被遍历
configurable:true // 可被删除
});
obj.a = 20;
console.log(obj.a); // 20 可被更改
for(let i in obj){
console.log(i); // a 可被遍历到
}
delete obj.a;
console.log(obj.a); // undefined a属性已经被删除
深拷贝和浅拷贝
深拷贝:对于引用类型的数据,拷贝值而不是地址。(即拷贝之后,二者的操作互不影响)
浅拷贝:对于引用类型的数据,直接令相等,拷贝的是地址。(其中任意一个操作,都会影响另一个,因为他们指向的是同一块地址空间)
1、递归的方式实现深拷贝。
function deepCopy(obj){
if(Array.isArray(obj)){
//是数组
var newObj = [];
}else{
//是对象
var newObj = {};
}
//遍历
for(var i in obj){
if(typeof obj[i] == "object"){
//是引用数据类型,则递归调用deepCopy()
newObj[i] = deepCopy(obj[i])
}else{
//不是引用数据类型,则是简单数据类型,直接赋值
newObj[i] = obj[i];
}
}
//处理之后的复制对象数据返回
return newObj;
}
var obj1 = {a:[1],b:2};
var newObj = deepCopy(obj1);
newObj.a.push(2);
console.log(newObj,obj1);
//{a:Array(2),b:2} {a:Array(1),b:2}
//此种方法,无论是多少层,都能实现深拷贝
2、简单方式实现深拷贝,先转为JSON格式的字符串,再把JSON格式的字符串转为JSON类型的数据。
var obj1 = {a:[1,[11]],b:2};
var obj = JSON.parse(JSON.stringify(obj1));
console.log(obj);
设计模式
1、单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。
var singleTon = (function(){
//定义一个_instance属性,保证只能有一个实例
var _instance = null;
function Foo(){
this.prop = "";
}
Foo.prototype.fn = function(){};
//返回对象,存放getInstance方法,只有被调用该方法时,才会执行new
return {
getInstance:function(){
if(!_instance){
_instance = new Foo();
}
//如果有实例存在,直接返回,没有实例,创建一个之后返回创建的实例
return _instance;
}
}
})();
//匿名函数自调用,直接执行
console.log(singleTon.getInstance()==singleTon.getInstance());
// true
2、观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
var observer = {
on:function(eventName,callback){
//定义一个obj对象,存放事件处理函数的对象
if(this.obj===undefined){
this.obj = {};
}
//某个事件可能有多个事件处理函数,故每个事件的属性值为数组格式
if(this.obj[eventName]===undefined){
this.obj[eventName] = [callback];
}else{
this.obj[eventName].push(callback);
}
},
emit:function(eventName){
//如果该事件在事件处理函数对象中存在
if(this.obj[eventName]){
//遍历该事件的属性值(数组格式)
for(var i = 0; i < this.obj[eventName].length; i++){
//调用执行事件处理函数
this.obj[eventName][i]();
}
}
},
remove:function(eventName,callback){
//如果该事件在事件处理函数对象中存在
if(this.obj[eventName]){
//遍历
for(var i = 0; i < this.obj[eventName].length; i++){
//找到要移除的那个事件处理函数,进行移除
if(this.obj[eventName][i]==callback){
this.obj[eventName].splice(i,1);
}
}
}
}
}
//个人中心模块注册了loginSuccess事件
observer.on("loginSuccess",function(){
console.log("个人中心模块发生了改变");
})
//购物车
observer.on("loginSuccess",function(){
console.log("购物车模块发生了改变");
})
//评论
observer.on("loginSuccess",foo)
function foo(){
console.log("评论模块发生了改变");
}
observer.remove("loginSuccess",foo)
// 移除掉评论模块
observer.emit("loginSuccess");
//个人中心模块发生了改变
//购物车模块发生了改变
3、适配器模式:将一个类(对象)的接口(方法或者属性)转化成另外一个接口以满足用户需求,使类(对象)之间接口的不兼容问题通过适配器得以解决。
(1)参数适配
function foo(a,b,c,d){
console.log(a,b,c,d);
}
var params = {
a:1,
b:2,
c:3,
d:4
}
//适配器
var _adapter = function(obj){
foo(obj.a,obj.b,obj.c,obj.d);
}
_adapter(params);
//能够通过参数适配器,实现不同格式的参数传递
(2)默认参数和传进来的参数适配
//opts 自定义参数 defaults默认
function foo(opts){
var defaults = {
a:1,
b:2,
c:3,
d:4
}
var _adapter = {};
//实现如果有默认参数值,使用默认参数值
//否则使用传进来的参数值
for(var i in defaults){
_adapter[i] = opts[i] || defaults[i];
}
//_adapter为结合了默认参数和传进来参数的对象
console.log(_adapter);
}
foo({a:10});
//{a: 10, b: 2, c: 3, d: 4}
(3)类的接口适配
var baiduMap = {
show:function(){
console.log("baidu");
}
};
var googleMap = {
display:function(){
console.log("google");
}
};
//定义一个适配器
var _googleMapAdapter = {
show:function(){
baiduMap.show();
googleMap.display();
}
}
_googleMapAdapter.show();
//baidu
//google
(4)数据适配
var data = [{week:1,pv:100},{week:2,pv:200},{week:3,pv:300}];
//要把week和pv分离处理成 [1,2,3] [100,200,300]这种格式
var _weekAdapter = data.map(function(item){
return item.week;
});
var _pvAdapter = data.map(item=>item.pv);
console.log(_weekAdapter);
//[1, 2, 3]
console.log(_pvAdapter);
//[100, 200, 300]
4、组合模式又称部分-整体模式,将对象组合成树形结构以表示“部分整体”的层次结构。
//酒店订单
function Hotel(){
}
Hotel.prototype.create = function(){
console.log("创建了酒店订单");
}
//机票订单
function Ticket(){
}
Ticket.prototype.create = function(){
console.log("创建了机票订单");
}
function Order(){
this.orders = [];
}
Order.prototype.add = function(order){
this.orders.push(order);
return this;
}
Order.prototype.create = function(){
for(var i = 0; i < this.orders.length; i++){
this.orders[i].create();
}
}
//两个机票订单 一个酒店订单
var order = new Order();
order
.add(new Ticket())
.add(new Ticket())
.add(new Hotel())
.create();
//2次打印 创建了机票订单
//创建了酒店订单
经典面试题
var Event = {
// 通过on接口监听事件eventName
// 如果事件eventName被触发,则执行callback回调函数
on: function(eventName, callback) {
//设置一个对象,存放所有的事件名和对应的事件处理函数
if(this.obj===undefined){
//this.obj = {};
Object.defineProperty(this,"obj",{
value:{},
//设置为不可枚举,这样使用Object.assign就不能复制到他的值
//实现了复制之后,相互之间的事件处理函数不影响
enumerable:false
})
}
//如果对象中不存在该事件名,则定义一个数组,存放该事件名的所有事件处理函数
if(this.obj[eventName]===undefined){
this.obj[eventName] = [callback];
}else{
this.obj[eventName].push(callback);
}
},
//触发事件 eventName
emit: function(eventName) {
//如果存在该事件名
if(this.obj[eventName]){
//遍历,执行对应的事件处理函数
for(var j = 0; j < this.obj[eventName].length; j++){
this.obj[eventName][j](arguments[1]);
}
}
}
};
// 测试1
Event.on('test', function(result) {
console.log(result);
});
Event.on('test', function() {
console.log('test');
});
Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'
// 测试2
var person1 = {};
var person2 = {};
Object.assign(person1, Event);
Object.assign(person2, Event);
person1.on('call1', function() {
console.log('person1');
});
person2.on('call2', function() {
console.log('person2');
});
person1.emit('call1'); // 输出 'person1'
person1.emit('call2'); // 没有输出
person2.emit('call1'); // 没有输出
person2.emit('call2'); // 输出 'person2'