javascript高级与设计模式

该博客围绕JavaScript展开,介绍了实例与静态属性方法、对象类型验证、继承方式如寄生组合式继承等知识。还阐述了浅拷贝和深拷贝的区别,进程与线程的关系。重点讲解了多种设计模式,包括工厂、单例、观察者等模式的概念、实现及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.实例属性和实例方法:

都是绑定在使用构造函数创建出来的对象p上,最终使用的时候也是使用对象p来进行访问。

比如:

function Person(name,age,func){

   this.name = name;

   this.age = age;

   this.func = func;

}

静态属性和静态方法:

绑定在函数身上的属性和方法。

函数本质上也是个对象,既然是个对象,那么就可以动态的添加属性和方法。

只要函数存在,那么绑定在它身上的属性和方法,也会一直存在。

function Person(name,age,func){

   this.name = name;

   this.age = age;

   this.func = func;

   if(!Person.personCount){

      Person.personCount = 0;

   }

   Person.personCount++;

}

Person.printPerson = function(){

   console.log('总共创建了' + Person.personCount + '个人');

}

 

 

2.打印内置对象

 

var arr = [1,2,3]

console.log(arr.constructor.name)//Array

console.log(Object.prototype.toString.call(arr))//Array

 

//自定义 new出来的对象用Object.prototype.toString.call(p)是不行的,返回的是[Object Object]

//自定义的构造函数,其实在内部实现了var this=new Object(),因此,最后返回的还是Object对象,给了this

function Person(name, age) {

   //var this=new Object()

   this.name = name

   this.age = age

}

var p = new Person()

console.log(Object.prototype.toString.call(p));//[Object Object]

 

//需要用

console.log(p.constructor.name) //Person

3.自己创建的对象类型验证

console.log(p instanceof Person)//true

 

4.访问函数原型对象的方式

(1)Person.prototype

(2)var p = new Person()

p.__proto__

 

5.hasOwnProperty和in

//in中会沿着自身查找,再进入原型链查找

function Person(name,age){

   this.name = name;

   this.age = age;

}

Person.prototype.address = "上海";

 

var p = new Person('ka',12);

console.log('name' in p);//true

console.log(''address" in p);//true

 

//hasOwnProperty只查找自身,不去原型链查找

var p = new Peson('ada',12);

console.log(p.hasOwnProperty('name'));//true

console.log(p.hasOwnProperty('address'));//false

 

6.constructor

function Person(){}

var p = new Person()

console.log(p)

Person.prototype = {

   //constructor:Person, //一定要设置,不然就指向Object了

   name:"sad",

   age:12

};

var p1 = new Person()

console.log(p1);

console.log(p1.constructor.name)

7.改变student原型对象的指向(即,student继承person)

function Student(){

   this.num = 'asda'

}

function Person(){

   this.run = function(){

      console.log('run')

   }

}

 

(1)

//让Student的原型对象--》Person的原型对象。但是父类构造函数无法访问

Student.prototype = Person.prototype

 

 

(2)

//1.构造父类的实例

var p = new Person()

//2.并设置为子类的原型对象,指向父类的实例

Student.prototype = p

//3.修复constructor指针

Student.prototype.constructor = Student

 

8.call和apply(都是Function的原型对象方法)

作用:借用方法实现

参数:第一个参数是调用该方法的对象

第二个参数:call:参数列表;apply:数组

 

var demo1 = {

   name:'老实人',

   logSomething:function(param1,param2){

      console.log(this.name,param1,param2);

   }

}

var demo2 = {

   name:'小偷'

}

//demo2使用demo1的logSomething方法

//call

demo1.logSomething.call(demo2,'采花大盗',‘怪侠’);

 

//apply

demo1.logSomethind.apply(demo2,['采花大盗',‘怪侠’]);

 

//运用继承

function Person(){

this.name = 'asd';

this.age = 18;

}

 

function Student(){

//1.借调

Person.call(this);//借调了Person内的属性

this.sex='男';

}

 

9.this总结

this总是指向一个对象

函数调用方式:

1.作为对象的方法来调用。 this-->当前的对象

2.作为普通的函数调用。 this-->window

3.作为构造函数和new使用。 this-->构造函数内部信创建的对象

4.被call或者是apply调用(函数上下文调用)。 this-->第一个参数

 

10.寄生组合式继承

function Person(){

this.name = 'asd';

this.age = 18;

}

 

function Student(){

   //1.借调

   Person.call(this);//借调了Person内的属性

   this.sex='男';

}

fucntion Temp(){}

Temp.prototype = Person.prototype

var stuPrototype = new Temp()

Student.prototype = stuPrototype

stuPrototype.constructor = Student

 

为什么要加入一个中间对象temp?

因为如果子类实例的原型对象直接指向父类的原型对象的话,那父类的构造函数的prototype也指向父类的原型对象,那两者都可以修改他,会乱套的。因此引入了中间对象temp

 

11.内置对象扩展方法

Array.prototype.push=function(){

}

这里自定义push方法,讲内置的push方法进行了覆盖。

 

最好用继承的方式去扩展内置对象方法

function MyArray(){}

MyArray.prototype = new Array();

MyArray.prototype.run = function(){

}

 

12.浅拷贝和深拷贝

浅拷贝:

只是拷贝了一份地址

var obj = {name:'lcb',age:11};

var copyObj = {};

 

for(var key in obj){

   copyObj[key] = obj[key];

}

// Object.assign(copyObj,obj);//将后面的参数赋给第一个参数

console.log(copyObj)

 

但是复杂的数据类型,比如 var obj = {name:'lcb',age:11,pets:['12','34']};

简单的浅拷贝是不行的,因为浅拷贝把pets的地址拷贝过来,动一个,两者都会动。因此这样的复杂的数据类型是不可以浅拷贝的。

 

深拷贝:

在堆区中再开辟一块新的空间。拷贝地址对应的具体内容。运用了递归的思想。

function DeepCopyObj2NewObj(fromObj,toObj){

   //先检测第一个参数是否有值

   //如果没有值就初始化一个空对象

   for(var key in fromObj){

      var fromValue = fromObj[key];

      if(!isObj(fromValue)){

         toObj[key] = fromValue;

      }else{

         var temObj = new fromValue.constructor;

         DeepCopyObj2NewObj(fromValue,temObj);

         toObj[key] = temOmasbj;

      }

   } 

}

 

function isObj(obj) {

return obj instanceof Object;

}

 

13.进程与线程

进程:程序的一次执行,已占有一片内存空间

线程:CPU的基本调度单位,是程序执行的一个完整流程

 

进程与线程的关系:1.一个进度至少有一个运行的线程:主线程;

2.一个进程中也可以同时运行多个线程,这时程序是多线程的;

3.一个进程内的数据可以供其中的多个线程直接共享;

4.多个进程之间的数据是不能共享的

 

浏览器运行时多进程的。

JS是单线程的。

优先执行初始化代码,遇到回调,则放到最后去执行。

 

事件循环模型:

 

 

14.设计模式

根本目的:减少项目变化所造成的影响。

概念:是为了解决在开发中可能遇到的需求,而提出的一套解决方案。

目的:高内聚、低耦合;提高重用性,减少代码冗余;扩展性;稳定性;

 

 

15.工厂模式

(1)简单工厂模式:给定不同的材料,生产不同属性值的产品,并把产品返回

批量生产

步骤:1.声明工厂函数(接受原料参数);2.使用工厂函数,传递参数,接受对象。

(2)复杂工厂模式:可以根据不同的类型,创建不同的产品

步骤:

(3)应用场景:需要创建的对象较少;客户端不关心对象的创建过程;对象的构建十分复杂;需要依赖具体环境创建不同实例;处理大量具有相同属性的小对象

(4)业务需求:网页端的Token存储:1.根据工厂模式,在工厂中创建对象,工厂是根据不同浏览器类型,创建不同的对象;

2.在浏览器支持H5的时候,存储token于localStorage;

3.在不支持h5的浏览器中还是存于cookie。

 

//安全的工厂方法:在构造函数开始是先判断当前对象this指代是不是类,如果是则通过new关键字创建对象,如果不是说明类在全局作用域中执行(this指向windows)

var Factory = function (type, content) {

   if(this instanceof Factory){

      return this[type](content);

   }else{

      return new Factory(type,content);

   }

}

Factory.prototype = {

java : function(content){

},

javaScript:function(content){

},

Ui:function(content){

   this.content = content;

   (function(content){

      var div = document.createElement('div');

      div.innerHTML = content;

      div.style.border = '1px solid red';

      document.getElementById('container').appendChild(div);

      })(content)

   }

}

 

 

//复杂的工厂模式

function FruitMaker() {}

FruitMaker.prototype.make = function (type,meta) {

   //1.工厂是否具备生产的能力

   if(typeof this[type] === 'function'){

      var func = this[type];

      func.prototype = FruitMaker.prototype;

      return new func(meta);

   }else{

      throw '很抱歉,工厂不能生产这个产品'

   }

}

//扩展生产线

FruitMaker.prototype.extend = function (obj) {

   for(var key in obj){

      this[key] = obj[key];

   }

}

FruitMaker.prototype.extend({

   "Apple":function (meta) {

      console.log('造了苹果汁,材料有:' , meta);

   },

   "Pear":function (meta) {

      console.log('造了苹果汁,材料有:' , meta);

   }

})

//1.实例化

var maker = new FruitMaker();

var appleObj = maker.make('Apple','一个苹果,一斤水');

console.log(appleObj)

 

 

16.单例模式

特点:在整个程序的运行过程中一个类型只有一个实例对象:1.通过制定的构造函数,无论创建多少次对象,都只有一个;2.全局的用户信息对象;3.登陆/注册(常见)

js实现单例模式实现方式:1.全局变量;2.静态属性;3.优化;4.闭包-惰性函数(1 2 多一些)

 

// 全局变量

var instance = null;

   function Tool() {

      //1.判断

      if(instance){

      return instance;

   }

   instance = this;

   this.name = 'asd';

   this.age = 12;

}

缺点:使用一个全局变量来保存单例对象,该全局变量在整个作用域中都可以被访问或者是修改,可能会轻易的被覆盖或者是修改。修改后,创建出来的实例对象就不再是之前的那个单例对象了。

 

//即时函数(用的比较多)

(function (w) {

   var instance = null;

   function Tool() {

      //1.判断

      if(instance){

         return instance;

      }

      instance = this;

      this.name = 'asd';

      this.age = 12;

   }

   w.Tool = Tool;

})(window);

 

//闭包-惰性函数

function Tool() {

   var instance = this;

   this.name = 'asd';

   this.age = 14;

   //惰性函数(只会被执行一次,之后被直接调用) 第一次进来对Tool进行赋值,第二次,直接进入Tool

   Tool = function(){

   return instance;

   }

}

 

缺点:写的原型方法调用不到。

var t1 = new Tool();

Tool.prototype.run = function(){}//已经执行了一次,instance有值

var t2 = new Tool():

 

//t1和t2无法调用run

 

解决办法:将添加到原型方法放到实例化之前

Tool.prototype.run = function(){}

var t1 = new Tool();

var t2 = new Tool():

 

 

改造:

function Tool() {

   var instance = this;

   var oldprototype = Tool.prototype;

   this.name = 'asd';

   this.age = 14;

   //惰性函数(只会被执行一次,之后被直接调用) 第一次进来对Tool进行赋值,第二次,直接进入Tool

   Tool = function(){

      return instance;

   }

   Tool.prototype = oldprototype;

   instance = new Tool():

   instance.constructor = Tool;

return instance;

}

 

17.观察者模式(发布-订阅模式)

一旦有人发布,订阅者就可以收到消息,主动权在发布者手中

//简单的观察者模式

lk = {

   //用户群体

   targetActions:[],

   //添加用户

   addUser:function (target,action) {

      var obj = {target:target,action:action};

      this.targetActions.push(obj);

   },

   publishMsg:function () {

      for(var i = 0,len = this.targetActions.length;i<len;i++){

         var obj = this.targetActions[i];

         var target = obj.target;

         var action = obj.action;

         action.call(target,'明天周末了')

      }

   }

};

function response(str){

   console.log('已推送',this.name,"消息为:"+ str);

}

 

var stu1 = {name:'zha'}

var stu2 = {name:'li'}

lk.addUser(stu1,response)

lk.addUser(stu2,response)

 

lk.publishMsg()

 

 

//复杂的观察者模式

lk={

   typeTargetAction:{},

   addUser:function (type,target,action) {

      if(typeof this.typeTargetAction[type] === 'undefined'){

         this.typeTargetAction[type] = [];

      }

      var obj = {target:target,action:action};

      this.typeTargetAction[type].push(obj);

   },

   publishMsg:function (type,msgContent) {

      var targetActions = this.typeTargetAction[type]||[];

      for(var i = 0,len = targetActions.length;i < len;i++){

         var obj = targetActions[i];

         var target = obj.target;

         var action = obj.action;

         action.call(target,msgContent);

      }

   }

}

var stu1 = {name:"123"}

var stu2 = {name:"345"}

lk.addUser('h5',stu1,function(msgContent){

   console.log(msgContent, '已经推送给',this.name)

})

lk.addUser('h5',stu2,function(msgContent){

   console.log(msgContent, '已经推送给',this.name)

})

lk.addUser('java',stu2,function(msgContent){

   console.log(msgContent, '已经推送给',this.name)

})

lk.publishMsg('h5','房间')

 

18.命名空间模式

写法就是吧所有的东西都写在一个对象里面

为什么要使用命名空间:为了防止全局变量的冲突

 

 

18.策略模式

var Celue = {

   slow:function (distance) {

      console.log('慢速', distance*2)

   },

   normal:function (distance) {

      console.log('正常',distance)

   },

   fast:function (distance) {

      console.log('快速',distance/2)

   }

};

function PersonRun(from, to) {

   this.from = from;

   this.to = to;

}

 

PersonRun.prototype.run = function (celue) {

   celue(this.to - this.from)

}

 

var p = new PersonRun(0,20)

p.run(Celue.slow)

 

19.模版模式

很多事情的流程步骤都是一模一样的,只是部分细节不同。此时可以在父类定义这个模板,封装这些固定的操作,子类型重写部分方法.

function Fruit(){

}

Fruit.prototype.make = function () {

   this.water();

   this.cailiao();

   this.jiaoban();

}

Fruit.prototype.water = function () {

   console.log('')

}

Fruit.prototype.cailiao = function () {

   throw new Error('')

}

Fruit.prototype.jiaoban = function () {

}

function Apple() {}

Apple.prototype = new Fruit();

Apple.prototype.cailiao = function () {

   console.log('放入')

}

var apple = new Apple();

console.log(apple);

apple.make();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值