JS笔记(三)_深浅拷贝、js原型

本文详细解析了JavaScript中的深拷贝与浅拷贝概念,通过四种拷贝方式的对比,展示了不同场景下的应用与局限。同时,深入探讨了JS的原型与原型链机制,以及如何利用构造函数、原型和寄生组合继承等方式实现对象的继承。

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

1.深浅拷贝

//同时参考:https://segmentfault.com/a/1190000008838101
//第一种对象拷贝(无法拷贝方法)
var obj = {
    name: 'daliu',
    age: 25,
    action: function(){}
}
console.log(JSON.stringify(obj))       //{"name":"daliu","age":25}
var obj2 = JSON.parse(JSON.stringify(obj))
obj2.name = 'xiaoliu'
console.log(obj) //{ name: 'daliu', age: 25, action: [Function: action] }
console.log(obj2) //{ name: 'xiaoliu', age: 25 }

//第二种对象拷贝(无法拷贝方法)
var a = {name: 'xm', age: 19}
var b = new Object()
b.name = a.name
b.age = a.age
b.age = 29
console.log(a)   //{ name: 'xm', age: 19 }
console.log(b)   //{ name: 'xm', age: 29 }

//第三种对象拷贝   Object.assign
var Animal = {
    name: 'jack',
    color: ['red','black'],
    say: function(){
        console.log(this.name)
    }
}
function copy(obj) {
    var o = {}
    Object.assign(o, obj)   //不能复制引用类型
    return o;
}
var Animal1 = copy(Animal)
var Animal2 = copy(Animal)
Animal1.name = 'chan'
Animal1.color.push('white')
console.log(Animal1) //{name: 'chan',color: [ 'red', 'black', 'white' ],say: [Function: say]}
console.log(Animal2) //{name: 'jack',color: [ 'red', 'black', 'white' ],say: [Function: say]}
//可以看到数组这种引用类型两者只是复制了一份索引,使用的是堆内存的同一个区域

//第四种:深度拷贝
var Animal = {
    name: 'jack',
    color: ['red','black'],
    say: function(){
        console.log(this.name)
    }
}
function deepCopy(dest, ori){  //dest:目标对象 , ori:源对象
    for(var i in ori){
        if(typeof ori[i] === 'object'){
            //递归,判断是数组还是对象
            dest[i] = (ori[i].constructor === Array) ? [] : {}  //初始化属性
            deepCopy(dest[i], ori[i])
        }else{
            dest[i] = ori[i]
        }
    }
    return dest
}
var Animal1 = deepCopy({}, Animal)
var Animal2 = deepCopy({}, Animal)
Animal1.name = 'chan'
Animal1.color.push('white')
console.log(Animal1)  //{name: 'chan', color: [ 'red', 'black', 'white' ], say: [Function: say]}
console.log(Animal2)  //{ name: 'jack', color: [ 'red', 'black' ], say: [Function: say] }

 

2.普通对象 / 函数对象

//函数对象
function f(){}
console.log(typeof f, f.prototype)   //function f {}
var fun = new f()
console.log(typeof fun, fun.prototype)  //object undefined

//普通对象
var o = {}
console.log(typeof o, o.prototype)  //object undefined

//观看函数对象和普通对象的区别引出下述关于原型的概念

 

3.JS原型

在java开发中关于继承的概念是父类和子类,JS也是一门面向对象的语言,因此绕不过继承的概念,而JS的设计者则给出的是原形及原型链的概念,我对此的简单理解就是为了能够实现继承,实现java中访问父类属性的概念,参考下述简单示例

 

var father = {
    name: 'fa',
    age: 45,
    skill: function(){
        console.log('I can work')
    }
}
var child = {
    name: 'child',
    age: 15
}
child.prototype = father           //实现child继承father
child.prototype.skill()            //I can work
console.log(child.name)            //child
console.log(child.prototype.name)  //fa

 

回到创建实例对象的几种方法,当使用构造函数时,如果存在相同的方法,则每个实例对象会存在方法的重复,而使用原型时,则共用的方法则不会占用多余内存,如下示例:

 

//1.工厂创建对象:  存在不能够确定对象类型的问题
function Cat(name){
    return {
        name: name,
        say: function(){
            console.log(this.name)
        }
    }
}
var cat = Cat('heimao')
console.log(cat instanceof Cat)   //false

//2.构造函数: 存在方法的重复
function Cat(name){
    this.name = name
    this.say = function(){
        console.log(this.name)
    }
}
var cat1 = new Cat("baimao")
var cat2 = new Cat("heimao")
console.log(cat2 instanceof Cat)  //true

//3.使用原型
function Cat(name) {
    this.name = name;
}
Cat.prototype.say = function(){
    console.log(this.name)
}
var cat1 = new Cat("baimao")
var cat2 = new Cat("heimao")
console.log(cat2 instanceof Cat)  //true

使用原型实现一个继承

function Animal(){
    this.type = '动物'
}
function Cat(name){
    this.name = name
}
Cat.prototype = new Animal()
var cat1 = new Cat("小花")
console.log(cat1.name, cat1.type)   //小花 动物

 

构造器实现继承

function Animal(){
    this.type = '动物'
}
function Cat(name){
    Animal.apply(this)         //把父类属性复制过来
    this.name = name
}
var cat1 = new Cat("小白")
console.log(cat1.name, cat1.type)   //小白 动物

组合继承(原型+构造器):调用了两次父类构造器

function Person(name){
    this.skill = 'eat'
    this.name = name 
}
Person.prototype.showName = function(){  //在Person的原型中增加一个方法
    console.log(this.name)
}
function Teacher(name, course){
    Person.call(this, name)          //第二次调用,把Person中的属性拷贝一份到Teacher中
    this.course = course
}
Teacher.prototype = new Person()     //第一次调用,把Teacher的原型指向Person,也就是让Teacher继承Person
var t1 = new Teacher('老刘', '语文')
console.log(t1.course, t1.name, t1.skill)   //语文 老刘 eat
t1.showName()  //在Teacher对象中找不到这个方法,则去原型(父类)中找,依然没有,则去Person的原型(父类)中找

 

组合继承(原型+构造+寄生)

function Person(name){
    this.skill = 'eat'
    this.name = name 
}
Person.prototype.showName = function(){  //在Person的原型中增加一个方法
    console.log(this.name)
}
function Teacher(name, course){
    Person.call(this, name)          //把Person中的属性拷贝一份到Teacher中
    this.course = course
}
function extend(subObj, superObj){
    //创建一个空对象,将空对象的原型指向superObj的原型
    var proObj = Object.create(superObj.prototype)
    subObj.prototype = proObj
}
extend(Teacher, Person);
var t1 = new Teacher('老汪', '数学')
console.log(t1.course, t1.name, t1.skill)   //数学 老汪 eat
t1.showName()

//对于这种方式的理解:
//对比上面原型+构造的方式,让Teacher继承自Person,则需要先创建一个Person实例,
//而这种方式只需创建一个空对象作为中间人把两者继承关系关联起来,形成原型链(继承链)
//根据JS高程书中的解释:
//上述原型+构造中,第一次调用时在teacher的原型中创建了name和skill的属性,
//第二次调用时则把原型中的属性又复制到了teacher中,增加了内存的消耗

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值