对象创建
1. 字面量方式
let obj={
name:"小明",
age:23,
hobby:function(){
console.log('乒乓球')
}
}
2. 构造函数
let obj=new Object()
obj.name="雄安"
obj.age=23
obj.hobby=function(){
console.log('乒乓球')
}
console.log(obj)
3. 通过Object.create( )
创建的属性和方法会被放在原型上
let obj=Object.create({
name:"小明",
age:23,
hobby:function(){
console.log('乒乓球')
}
})
console.log(obj)
// 打印价格:
{}
__proto__:
age:20
hobby:f hobby()
name:"小明"
__proto__:Object
对象的调用:
1. 通过 .
console.log(obj.name)
obj.hobby()
2. 通过 []
console.log(obj['name'])
.与[]的区别:
[]中可以是一个变量,但是.后面必须是对象中已经定义的属性名或者方法名
列如:
let str='name'
let obj={
[str]:"小明", // 这里的[]中可以尝试字符串的连接和简单运算
age:23,
hobby:function(){
console.log('乒乓球')
}
}
console.log(obj)
工厂模式:
1. 通过字面量方式
let xiaoM={
name:"小明",
age:23,
hobby:function(){
console.log('乒乓球')
}
}
let xiaoH={
name:"小红",
age:21,
hobby:function(){
console.log('篮球')
}
}
字面量创建会出现代码冗余
2. 通过工厂模式
function Preson(name,age,hobby){ // 这个Preson相当于一个类
let obj={} // 添加原料
obj.name=name // 加工原料
obj.age=age // 加工原料
obj.hobby=function(){ // 加工原料
console.log(hobby)
}
return obj // 出厂
}
let xiaoM =Preson("小明",23,"乒乓球") // 这里就相当于定义一个对象
let xiaoH =Preson("小红",21,"篮球")
console.log(xiaoM)
console.log(xiaoH)
能提高代码的复用性
使用new 运算符后有那些操作【使用new简化工厂模式】
new 的过程叫做实例化
1. 能执行函数
function(){
console.log("test")
}
test() // 执行函数
new test() // 执行函数
new test // 执行函数 不需要传递参数时,可以不带括号
2. 自动创建一个空对象
3. 把空对象和this绑定
4. 如果没有返还,隐式返还this
function Preson(name,age,hobby){
// let obj={} // 使用new后,自动创建一个空对象
this.name=name // 把空对象和this绑定
this.age=age
this.hobby=function(){
console.log(hobby)
}
//return obj // 如果没有返还,隐式返还this
}
let xiaoM =new Preson("小明",23,"乒乓球") // 这里与上面的输出相同
let xiaoH =new Preson("小红",21,"篮球")
构造函数
function Preson(name,age,hobby){
this.name=name
this.age=age
this.hobby=function(){
console.log(hobby)
}
}
let xiaoM =new Preson("小明",23,"乒乓球") // 这里new一个构造函数
构造函数的特点:
1. 首字母大写
new 的过程叫做实例化
2. this指向实例化对象【上面代码中的this指向的xianM 】
3. 静态属性和方法(属于类本身的)
Preson.num=0 // 静态属性
Preson.fn=function(){ // 静态方法
console.log('fn')
}
构造函数涉及的性能问题:
function Preson(name){
this.name=name
this.age=20
this.hobby=function(){
console.log("打球")
}
}
let xiaoM =new Preson("小明")
let xiaoH =new Preson("小红")
console.log(xiaoM.hobby===xiaoH.hobby) // false
这里两个对象在内存的地址不同,如果有100个对象,就会开辟100个新的内存地址,会造成性能的浪费,解决方法是: js提供了公共空间,存放相同方法,目的是更好的节约内存
这个公共空间就叫做:原型
原型
作用就是为了解决性能问题,更好的节约内存空间
function Preson(name){
this.name=name
this.age=20
//this.hobby=function(){
// console.log("打球")
// }
}
每一个构造函数new【也就是实例化】的过程,他都是由两部分构成
一部分是构造函数Preson
一部分提供一个公共空间 原型
每一个实例对象都可以去取
// 这里面的代码会放在一个空间里
Preson.prototype.hobby=function(){
console.log("打球")
}
let xiaoM =new Preson("小明")
let xiaoH =new Preson("小红")
// 这里的xiaoM,xiaoH两个对象中的每一个都是由两部分构成,一部分是构造函数Preson,一部分是一个公共空间,原型【打印这个对象后,可以在__proto_查看到_hobby方法】
console.log(xiaoM.hobby===xiaoH.hobby) // true
console.log(xiaoM)
// 打印xiaoM这个对象后,会发现,hobby方法是放在__proto__中的
console.log(xiaoM.__proto__===Preson.prototype) // true
new之前原型通过Preson.prototype去定义,new实例化之后,原型通过实例化的对象xiaoM.__proto__去获取
原型有一个固有属性 constructor 【系统定义好的】
console.log(Person.prototype.constructor===Person) // true
let zhangSan=new Person("张三")
console.log(zhangSan.constructor===Person) // true
通过constructor判断类型
let str='abc'
console.log(str.constructor===String) // true
工厂模式与构造函数的区别:
1. 工厂模式没有原型,没有提供公共空间,比较消耗性能,占用内存
2. 工厂模式没有constructor
call,apply,bind
function foo(name,age){
console.log(this,"姓名"+name+"年龄"+age)
}
let obj={
name="张三"
}
foo.call(obj,"张三",20)
foo.apply(obj,["张三",20])
foo.bind(obj)("张三",20) // bind调用返回一个函数,在加一个括号是函数的调用
继承
function Did(name,age){
this.name=name
this.age=age
this.money="10000"
}
function Son(name,age){
//Dad.call(this,name,age)
//Dad.apply(this,[name,age)
Dad.bind(this)(name,age)
this.sex="男”
}
let zhangSan =new Son("张三",23)
console.log(zhangSan.money) //10000
console.log(zhangSan.sex) //男
深拷贝 JSON.parse(JSON.stringify(obj))
缺点:当obj 中存在值为function和undefined时会丢失数据
手写深拷贝
let obj={
name:"张三",
age:20,
fu:function(){
console.log('fn')
},
arr:[]
}
function deepCopy(obj){
let newObj =Array.isArray(obj)?[]:{} // 判断obj是不是数组,是就创建数组[],否则创建对象
for(let key in obj){
// 这里需要注意,通过for in 循环对象,不仅会循环自身的属性和方法,还会循环原型以及原型链上面的属性和方法,我们做深拷贝是不需要,所以加个if判断
if(obj.hasOwnProperty(key)){ // 判断循环出来的属性和方法是不是对象自身的
// 如果是,才继续执行深拷贝
if(typeof obj[key] ==="object"){ // 判断是否为复杂数据类型
newObj[key]=deepCopy(obj[key])
}else{
newObj[key]=obj[key]
}
}
}
return newObj
}
// 测试深拷贝代码:
let obj2=deepCopy(obj)
obj2.name='李四'
console.log(obj)
console.log(obj2)
组合继承
let Link=function(){ } // 定义一个新的函数
Link.prototype=Dad.prototype
Son.prototype=new Link()