javascripts知识点梳理5

javascripts知识点梳理5

JS面向对象

ECMAScript 有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。

js(如果没有作特殊说明,本文中的js仅包含ES5以内的内容)本身是没有class类型的,但是每个函数都有一个prototype属性。prototype指向一个对象,当函数作为构造函数时,prototype则起到类似class的作用。

面向过程:以前大部分的代码都是面向过程的

面向对象:使用内置对象功能,jQuery

面向对象的三个特征

封装:封装为对象(工具的封装操作),需要调用即可,无需多次重写。

继承:子类可以继承父类的属性和方法 ,为了实现代码更好的复用

多态(多状态):让代码操作更加灵活

  1. 重载:严格意义上说JS中没有重载的功能,不过我们可以通过判断函数的参数的不同来实现不同的功能来模拟重载。
  2. 重写:子类可以改写父类的属性和方法

在这里我们可以理解为创造对象的几种模式:

创造对象的几种模式

单例模式,工厂模式,构造函数模式,原型模式等。

单例模式

小王在一个小公司,就自己一个前端,所以他写js都是这样的

1
2
3
4
var a = 1;
function getNum(){
return 1;
}

后来公司又招了个前端小明,于是变成他们2个一起写同一个js了。一天小王发现自己写的getNum方法出问题了,原来是小华写的js中也有个getNum的函数,代码合并后把他的覆盖掉了,于是便找小华理论去,经过一番妥协后,两人都把自己的代码改了改

1
2
3
4
5
6
7
8
9
10
11
12
13
var xiaoming = {
num:1,
getNum:function(){
return 1;
}
}

var xiaohua = {
num:2,
getNum: function(){
return 2;
}
}

这就是我们所谓的单例模式(命名空间)

单例模式的优劣

  1. 使用单例模式,我们可以实现简单的模块化开发
1
2
3
4
5
6
7
8
9
10
11
var utils = {
getCss:function(){
//code
},
getByClass:function(){
//code
},
setCss:function(){
//code
}
}

我们可以把自己写好的工具方法放到一个单独的js文件中,然后直接引入即可。

  1. 避免了全局变量名的冲突
    需要注意的是,我们在引入各个模块的时候,需要注意引入的顺序,引入顺序是按照各模块之间的相互依赖进行前后排列的;
  2. 缺点:
    • 单例模式只是一定程度上避免了变量名的冲突,但并不能彻底解决此问题,而且在不同的对象下,我们可能会有很多功能相同的代码,最终造成大量的冗余代码。
  • 单例模式让每个对象有了自己独立的命名空间,但是并不能批量生产的问题,每一个新的对象都要重新写一份一模一样的代码。

工厂模式

  1. 工厂模式其实就是把需要一个个的编写的对象,放在一个函数中统一的进行创建,说白了就是普通函数的封装。
  2. 工厂模式总共3步骤:
1
2
3
1)引进原材料 --- 创建一个空对象
2)加工原材料 --- 加工对象:给对象添加属性和方法;
3)输出产品 --- 返回对象:return 对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function CreatePerson(name,age){
var obj={};//1.创建一个空对象
//2.加工对象
obj.name=name;
obj.age=age;
obj.showName=function(){
console.log('我的名字是:'+this.name)
};
return obj;//3.输出对象;
}
var person1 = CreatePerson('小明',23)
var person2 = CreatePerson('小华',23)
person1.showName(); //我的名字是:小明
person2.showName(); //我的名字是:小华

工厂模式的优缺点

    1. 既然叫工厂模式,它就和我们周围的工厂一样,我们只需要把原材料放进去,就能得到我们需要的产品了。
    2. 工厂模式也解决了单例模式的批量生产的问题,避免了单例模式中的大量冗余代码,进行系统的封装,提高代码的重复利用率
    3. 不过工厂模式跟我们js内置类的调用方法不同

构造函数模式

  1. 可以创建一个自定义的类,并且可以new出实例
  2. 构造函数做的就是类和实例打交道。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 //构造函数:首字母大写(约定俗成);
function CreatePerson(name,age){ //创建一个自定义的类
//构造函数中的this,都是new出来的实例
//构造函数中存放的都是私有的属性和方法;
this.name=name;
this.age=age;
this.showName=function(){
console.log('我的名字是:'+this.name)
}
}
//实例1
var person1 = new CreatePerson('小明',25)
//实例2
var person2 = new CreatePerson('小华',24)

这里说一下工厂模式和构造函数模式的区别:

1
2
3
4
5
6
7
8
9
1. 在调用的时候不同:
工厂模式:调用的时候,只是普通函数的调用createPerson();
构造函数模式:new CreatePerson();
2. 在函数体内不同:
工厂模式有三步:1)创建对象 2)加工对象 3)返回对象;
构造函数模式只有1步: 只有加工对象; 因为系统默认会为其创建对象和返回对象;
3. 构造函数默认给我们返回了一个对象,如果我们非要自己手动返回的话:
(1)手动返回的是字符串类型:对以前实例上的属性和方法没有影响;
(2)手动返回的是引用数据类型:以前实例身上的属性和方法就被覆盖了;实例无法调用属性和方法;

我们可以使用原型模式来解决每个实例中都有相同方法的函数的问题

原型模式

我们把公有的方法放到函数的原型链上

1
2
3
4
5
6
7
8
9
10
11
function CreatePerson(name,age){ 
this.name=name;
this.age=age;
}
// 我们把公有的方法放到函数的原型链上
CreatePerson.prototype.showName = function(){
console.log('我的名字是:'+this.name)
}
var person1 = new CreatePerson('小明',25)
var person2 = new CreatePerson('小华',24)
person1.showName() //小明

原型模式的关键:

  1. 1)每个函数数据类型(普通函数,类)上,都有一个属性,叫prototype。

  2. 2)prototype这个对象上,天生自带一个属性,叫constructor:指向当前这个类;

  3. 3)每个对象数据类型(普通对象,prototype,实例)上都有一个属性,

  4. 叫做__proto__:指向当前实例所属类的原型

一.创建对象

创建一个对象,然后给这个对象新建属性和方法。

1
2
3
4
5
6
7
8
var box = new Object(); //创建一个Object 对象
box.name = 'Lee'; //创建一个name 属性并赋值
box.age = 100; //创建一个age 属性并赋值
box.run = function () { //创建一个run()方法并返回值
return this.name + this.age + '运行中...';
};
alert(box.run()); //输出属性和方法的值

上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box 对象本身。这种是JavaScript 创建对象最基本的方法,但有个缺点,想创建多个类似的对象,就会产生大量的代码。 为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createObject(name, age) { //集中实例化的函数
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name + this.age + '运行中...';
};
return obj;
}
var box1 = createObject('Lee', 100); //第一个实例
var box2 = createObject('Jack', 200); //第二个实例
alert(box1.run());
alert(box2.run()); //保持独立

工厂模式解决了重复实例化的问题,但是它有许多问题,创建不同对象其中属性和方法都会重复建立,消耗内存;还有函数识别问题等等。

二.构造函数的方法

构造函数的方法有一些规范: 1)函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和 普通函数); 2)通过构造函数创建对象,必须使用new 运算符。

1
2
3
4
5
6
7
8
9
10
11
12
function Box(name, age) { //构造函数模式
this.name = name;
this.age = age;
this.run = function () {
return this.name + this.age + '运行中...';
};
}
var box1 = new Box('Lee', 100); //new Box()即可
var box2 = new Box('Jack', 200);
alert(box1.run());
alert(box1 instanceof Box); //很清晰的识别他从属于Box

构造函数可以创建对象执行的过程: 1)当使用了构造函数,并且new 构造函数(),那么就后台执行了new Object(); 2)将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this 就 代表new Object()出来的对象。 3)执行构造函数内的代码; 4)返回新对象(后台直接返回)。

注:

1)构造函数和普通函数的唯一区别,就是他们调用的方式不同。只不过,构造函数也是函数,必须用new 运算符来调用,否则就是普通函数。

2)this就是代表当前作用域对象的引用。如果在全局范围this 就代表window 对象,如果在构造函数体内,就代表当前的构造函数所声明的对象。

这种方法解决了函数识别问题,但消耗内存问题没有解决。同时又带来了一个新的问题,全局中的this 在对象调用的时候是Box 本身,而当作普通函数调用的时候,this 又代表window。即this作用域的问题。

三.原型

我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

1
2
3
4
5
6
7
function Box() {} //声明一个构造函数
Box.prototype.name = 'Lee'; //在原型里添加属性
Box.prototype.age = 100;
Box.prototype.run = function () { //在原型里添加方法
return this.name + this.age + '运行中...';
};

所以,它解决了消耗内存问题。当然它也可以解决this作用域等问题。

我们经常把属性(一些在实例化对象时属性值改变的),定义在构造函数内;把公用的方法添加在原型上面,也就是混合方式构造对象(构造方法+原型方式):

1
2
3
4
5
6
7
8
9
var person = function(name){
this.name = name
};
person.prototype.getName = function(){
return this.name;
}
var zjh = new person(‘zhangjiahao’);
zjh.getName(); //zhangjiahao

内置对象添加方法

直接给内置构造函数原型添加即可

例如:Array.prototype.getSum = function () {}

模块化

表示将代码根据功能分为不同区域
使用立即执行函数对不同功能的代码进行包裹
使用一个js库requirejs
用于解决引入顺序问题

在html页面中引入require文件,并设置data-main属性声明入口模块路径

1
<script src ="require.js" data-main="main"></script>
1
2
3
4
在入口模块中声明依赖关系,参数1为地址,参数2为函数
require(['js/game'],function(Game){
......
})
1
2
3
4
5
在模块中需要被其他模块调用的进行返回值设置
define(['js/food' , 'js/snake'] , function(Food , Snake){
......
return Game;
})

高级技巧_判断是数组还是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // console.log(a.consturctor);
// function fn(){
var a = [1,2,3,4,5,6];
var c = {
name:'zs',
age:18
}
var b = c.constructor

console.log(b);


var a = [1,2,3,4,5,6];
var b = a.constructor === Array ? [] :{};
console.log(b);

因为 数组 和 对象都属于函数 函数就有原型对象
利用原型对象里的constructor 属性指向

javascripts知识点梳理5
http://www.dwyblog.cn/2019/04/02/javascripts知识点梳理5/
作者
幸福来敲门
发布于
2019年4月2日
许可协议