单例模式
把描述同一个事物特征的信息进行分类归组,放到同一个命名空间下(减少全局变量的污染)
var name
= "邢";
var age
= 21;
var sex
= "男";
var name
= "刘";
var age
= 21;
var sex
= "男";
var xing
= {
name
: "邢",
age
: 21,
sex
: "男"
};
var liu
= {
name
: "刘",
age
: 21,
sex
: "男"
};
var timer
= 1;
var xing
= {
bar
: function () {
},
fn
: function () {
}
};
var liu
= {
bar
: function () {
}
};
-----------------------------------------------------
var name
= 'erYa';
var age
= 18;
var name
= 'jinYu';
var age
= 22;
let person1
= {
name
: 'erYa',
age
:18
};
let person2
= {
name
: 'jinYu',
age
: 22
}
高级单例模式
var utils
= (function () {
var timer
= 1;
var num
= 100;
return {
bar
: function () {
},
fn
: function () {
}
}
})()
--------------------------------------------------
let person1
= (function(){
let fn = function(){};
let name
= 'erYa';
return {
name
:name
,
age
:18,
fn
:fn
}
})()
let person2
= (function(){
let fn = function(){};
let name
= 'jinYu';
let age
= 18;
return {
name
:name
,
fn
:fn
}
})()
let person3 = function(){
let name
= 'jinYu';
let age
= 26
return {
name
:name
,
fn
:fn
}
}
console
.log(person3
)
工厂模式
如果用单例模式去写很多个person就会变得很麻烦,就有了工厂模式 特点:批量生产把实现相同功能的代码封装到函数里,以后想运行这个方法,就直接执行这个函数就好了高内聚:提高代码的复用率低耦合:减少页面的重复代码
function createStudent(name
, age
) {
var obj
= {}
obj
.name
= name
;
obj
.age
= age
;
return obj
;
};
var xing
= createStudent('邢', 21);
var liu
= createStudent('刘', 21);
对象前言
javaScript 面向对象: 基于对象 对象: 万物皆对象 类: 指具有相同特征的一类事物 实例:具体的一个事物:
内置类 : Number String Boolean Null Undefined Object Array RegExp Date …类一定是个函数,但函数不一定是类;
面向对象
把抽象的对象按照特点进行分类(大类/小类),把类的公共特征进行提取和封装,放到对应的类别中类就是对对象的一种细分,和公共部分的抽取在类中具体派生出来的具体事物就是类的实例,而且实例拥有自己私有的特征,还拥有所属类上的特征我们研究面向对象,其实就是研究对象、类、实例之间的关系和各自的知识点
构造函数模式(构造自定义类)
构造函数解决了实例的私有属性
var f=new Fn();new 操作符——把 new 放在函数的前面,函数执行构造函数就是让我们创建自定义类new 函数执行 叫做构造函数运行模式,此时的Fn就是Fn类(构造函数),函数执行之后的返回结果就是一个对象,叫做实例对象(f就是Fn的实例)类就是函数数据类型的实例是对象数据类型构造函数中的this指向当前实例
如果这个函数需要参数,那么这个需要有小括号,如果不需要参数,那么小括号可以省略 new后面的函数就是构造函数,也叫类;那么通过new函数得到的返回值就叫实例;实例是构造函数new出来的;
> 钩子函数:在初始化实例时会默认调用原型上的一些方法,那么这些方法就是钩子函数
运行原理
1、在代码执行之前,函数中会首先默认创建一个空对象 {}2、让当前函数里的this指向这个对象3、代码执行4、默认return 这个对象
let f1 = new Fn(‘erYa’, 18); //原型三句话 //f1就是Fn的实例 //fn就是函数,也是自定义(Fn)类 //f1就是实例化的对象
//let f3 = new Fn; 如果构造函数不传实参,可以省略执行小括号 new 函数执行 叫做构造函数运行模式,此时的Fn就是Fn类(构造函数),函数执行之后的返回结果就是一个对象,叫做实例对象(f就是Fn的实例)
function fn() {
this.name
= 100;
};
var f
= new fn;
fn();
console
.log(f
);
function Fn(name
, age
) {
this.name
= name
;
this.age
= age
this.say = function(){}
return {}
}
Fn()
let f
= new Fn()
let f1
= new Fn('erYa', 18);
let f2
= new Fn('jinYu', 18);
let f3
= new Fn;
let f4
= Fn
;
console
.log(f
)
console
.log(f1
)
console
.log(f2
)
console
.log(f1
.age
=== f2
.age
)
console
.log(f1
.say
=== f2
.say
)
console
.log(f3
)
console
.log(f4
)
构造函数和普通函数的不同
JS为了区分构造函数和普通函数,一般将构造函数首字母大写;
运行上的不同
普通函数–>形成私有作用域–>形参赋值–>变量提升–>代码执行–>作用域是否销毁构造函数–>形成私有作用域–>形参赋值–>变量提升–>默认生成一个对象–>把this指向这对象–>代码执行–>默认把这个对象return出去–>作用域是否销毁 执行上的不同
构造函数如果不传实参,可以不加小括号 构造函数如果手动return一个基本数据值,不能改变人家的返回值,但是手动return引用数据类型,可以改变构造函数的返回值,此时return的东西已经不是当前类的实例了【所以不要轻易修改构造函数的返回值】
function Fn(n
) {
let m
= 10;
this.total
= m
+ n
;
this.say = function () {
console
.log(this.total
)
}
}
let f1
= new Fn(10);
let f2
= new Fn(20);
console
.log(f1
.n
)
console
.log(f2
.m
)
console
.log(f1
.total
)
f2
.say()
console
.log(f1
=== f2
)
创建实例的方式
字面量创建实例的方式
let num
= 1;
let str
= 'w';
构造函数创建实例的方式
var num
= new Number(1);
console
.log(typeof num
);
console
.log(num
instanceof Number);
console
.log(num
);
console
.log(num
+1);
console
.log(num
== 1 )
var arr
= new Array(100,200);
console
.log(arr
);
console
.log(typeof Array
);
var arr
= new Array("a")
console
.log(arr
);
------------------------------------------------------------------------
let ss
= new Number(1)
console
.log(ss
)
console
.log((1).toFixed(2))
console
.log(ss
.toFixed(2))
let w
= new String(3)
console
.log(w
)
console
.log(w
.substr
)
console
.log(w
instanceof String)
检测当前实例是否属于某个类
使用instanceof是检测当前实例是否属于某个类实例 instanceof 类,如果实例是属于这个类,那就返回true,反之就是false通过字面量方式创建的基本数据类型值不是一个标准的实例,不能使用instanceof 进行检测;引用数据类型创建的就是一个标准的实例,可以使用instanceof来进行检测;
局限性
instanceof不能检测基本数据类型,只能检测引用数据类型的值
--------------------------------------------
function Fn(name
, age
){
this.name
= name
;
this.age
= age
;
}
let f1
= new Fn;
console
.log(f1
instanceof Fn)
console
.log(1 instanceof Fn)
给实例的类封装公共方法需要注意的几点
你自己封装的方法不能与人家内置的方法同名给你自己的方法加前缀
function myUnique(){
console
.log(this)
let obj
= {};
for (var i
= 0; i
< this.length
; i
++) {
if(obj
[this[i
]] !== undefined
){
this[i
] = this[this.length
-1];
this.length
--;
i
--;
continue;
}
obj
[this[i
]] = this[i
]
}
}
Array
.prototype
.myUnique
= myUnique
;
console
.log(ary
.myUnique().sort().reverse().slice(1,3))
原型模式
原型模式: 构造函数解决了对象实例中私有属性的问题,原有模式解决了对象实例中公有属性的问题;如果将实例的私有属性放到了公有属性上,减少了堆内存的开辟;
原型解决了实例的公有属性
每一个函数(普通函数,构造函数)都天生自带一个prototype属性,属性值是一个对象,它里面存储的是实例的公有属性(原型)每一个原型都天生自带一个constructor属性,其属性值指向当前原型所属的类每一个对象都天生自带一个__proto__属性,其属性值指向当前实例所属类的原型
原型链
在对象里查找-一个属性,先看自己私有的有没有,如果自己没有,就通过__ proto__ 属性找 到当前所属类的原型上,如果原型上有,就直接用,如果没有,继续通过原型的__ proto__ 继续往所属类的原型上找,直到找到Object类的原型上找,如果还没有,就是undefined, 这种级- 一级一级向上查找就会形成原型链
function Fn(x
, y
) {
this.x
= x
;
this.y
= y
;
this.getX = function () {
console
.log(this.x
);
}
}
Fn
.prototype
.getX = function () {
console
.log(this.x
);
}
Fn
.prototype
.getY = function () {
console
.log(this.y
);
}
var f1
= new Fn(100, 200);
f1
.__proto__
.a
= 300;
var f2
= new Fn(100, 200);
console
.log(f1
.getX
== f2
.getX
);
console
.log(f1
.getY
== f2
.getY
);
console
.log(f1
.x
== f2
.x
);
console
.log(Fn
.prototype
.getX
=== f1
.getX
);
console
.log(Fn
.prototype
.getX
=== f1
.__proto__
.getX
);
f1
.getX()
Fn
.prototype
.getX();
f2
.__proto__
.getX();
Fn
.prototype
.getY();
console
.log(f1
.a
);
console
.log(f1
.toString
);
原型:
window 是全局作用域中一个大的对象;window是Object的一个实例;Function是所有函数数据类型的基类(包括自己)Object是所有对象的基类所有的函数都有prototype和__proto__属性Object的原型的__proto__执行的是自己,js认为自己指向自己没有意义,就规定为null所有的函数数据类型(普通的函数、类【内置类,自定义类】)都是Function的一个实例,Function和Object都是Function的一个实例,那Object类的__proto__指向Function的原型所有函数都是Function的实例, 那Function也是函数, 那他的__proto__指向的是自己的原型。它的原型是一个匿名函数,但是也是对象,正常使用即可
当函数作为对象时,有length和name属性
length:代表形参的个数,name:代表函数的名字 如果一个对象你不知道谁构出来的, 那他的__proto__就指向Object内置类的原型( 所有类的原型都指向Object内置类的原型)函数的三种角色: 普通函数 构造函数 普通对象在正式场合函数是以函数的身份出场的( 这是人家的主角色)类的特点: 多态、 继承和封装给Fn的原型新增键值对;这就是原型扩展;
原型图
原型链:单向不可逆
原型重定向
内置类的原型不能被重定向可以覆盖内置类原型上的方法用新的空间地址覆盖Fn原有的空间地址;会导致constructor的丢失;重定向例子
function Fn() {
this.x
= 100;
this.y
= 100;
}
Fn
.prototype
.getX = function () {
console
.log(this.x
)
}
let f1
= new Fn;
Fn
.prototype
= {
getY
: function () {
console
.log(this.y
)
}
};
let f2
= new Fn;
console
.log(f1
.getX
)
console
.log(f2
.getX
)
console
.log(f1
.constructor
)
console
.log(f2
.constructor
)
console
.log(Fn
.prototype
)
阿里面试题(原型链、运算符优先级)
<!DOCTYPE html
>
<html lang
="en">
<head
>
<meta charset
="UTF-8">
<title
>Document
</title
>
</head
>
<body
>
<script
>
function Foo() {
getName = function () {
console
.log(1);
};
return this;
}
Foo
.getName = function () {
console
.log(2);
};
Foo
.prototype
.getName = function () {
console
.log(3);
};
var getName = function () {
console
.log(4);
};
function getName() {
console
.log(5);
}
Foo
.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
</script
>
</body
>
</html
>