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中,增加了内存的消耗