JavaScript 对象

250 阅读4分钟

对象创建

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()

console.log --> 会转字符串打印出来 【不确定】

console.dir --> 会把方法都打印出来