前端面试day3

前面一直用存货,现在把最后的东西都放出去了,又该开始学习新内容了

23深拷贝和浅拷贝

内存

内存分为四个区域:栈 堆 全局静态区 只读区

假定只有局部变量,只看堆和栈,

1)、 定义局部变量 age,由于age是局部变量,所以在栈中申请内存空间,起名为age,又由于给age赋的值250是基本类型,所以,值直接存储在栈中。

2)、定义局部变量arr,由于arr是局部变量,所以在栈中申请空间,但是arr的内存中存储的是什么?由于给arr赋的值不是基本类型,而是引用类型(new出来的),所以,先在堆中申请空间存放数据 12,23,34,。再把堆区的地址赋给arr。

赋值

如果给arr[0]赋值的话,arr1[0]的值也会发生变化,因为,arr和arr1保存着相同的地址,它门两个引用的数据是共享的。就像你在很多地方(简历的那张纸,户口本上的那张纸)会写上你的家庭地址。这么多张纸都引用着你家。根据一张纸上找到你家,给你家放上一百万的现金(数据改变了,相当于arr[0]=10),再根据另外一张纸的地址也找到了你家,你发现你一百万在(不要给我说被人拿了)

如果在上面的基础上增加一句代码:arr[0]=10;那么内存将会有如下变化:

深拷贝和浅拷贝:

其实在第二点已经说到了拷贝,所谓拷贝,就是赋值。把一个变量赋给另外一个变量,就是把变量的内容进行拷贝。把一个对象的值赋给另外一个对象,就是把一个对象拷贝一份。

1.基本类没有问题,

因为,基本类型赋值时,赋的是数据(所以,不存在深拷贝和浅拷贝的问题)。

如:

Var x = 100;

Var y = x; //此时x和y都是100;

如果要改变y的值,x的值不会改变。

2.引用类型有问题

因为,引用类型赋值时,赋的值地址(就是引用类型变量在内存中保存的内容)

var arr1 = new Array(12,23,34)

Var arr2 = arr1;//这就是一个最简单的浅拷贝

如果要改变arr2所引用的数据:arr2[0]=100时,那么arr1[0]的值也是100。

原因就是 arr1和arr2引用了同一块内存区域(以上的第二点中有体现)。

这是最简单的浅拷贝,因为,只是把arr1的地址拷贝的一份给了arr2,并没有把arr1的数据拷贝一份。所以,拷贝的深度不够

3.用json对象的方式(也是引用类型)来演示浅拷贝和深拷贝

var p = {

"id":"007",

"name":"刘德华",

"books":new Array("三国演义","红楼梦","水浒传")//这是引用类型

}

深拷贝

var p2 = {};

for(let key in p){

if(typeof p[key]=='object'){

p2[key]=[];//因为,我上面写的是数组,所以,暂时赋值一个空数组.

for(let i in p[key]){

p2[key][i] = p[key][i]

}

}else{

p2[key] = p[key];

}

}

p2.books[0] ="四国";

console.log(p2);

console.log(p);

递归实现深拷贝

var p = {

"id":"007",

"name":"刘德华",

"wife":{

"id":"008",

"name":"刘德的妻子",

"address":{

"city":"北京",

"area":"海淀区"

}

}

}

//写函数

function copyObj(obj){

let newObj={};

for(let key in obj){

if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归

newObj[key] = copyObj(obj[key])

}else{//基本类型,直接赋值

newObj[key] = obj[key];

}

}

return newObj;

}

let pNew = copyObj(p);

pNew.wife.name="张三疯";

pNew.wife.address.city = "香港";

console.log(pNew);

console.log(p);

深拷贝_如果属性是数组等非键值对的对象

就得单独处理:要么给数组增加一个自我复制的函数(建议这样做),要么单独判断。

//给数组对象增加一个方法,用来复制自己

Array.prototype.copyself = function(){

let arr = new Array();

for(let i in this){

arr[i] = this[i]

}

return arr;

}

var p = {

"id":"007",

"name":"刘德华",

"books":new Array("三国演义","红楼梦","水浒传")//这是引用类型

}

function copyObj(obj){

let newObj={};

for(let key in obj){

if(typeof obj[key] =='object'){//如:key是wife,引用类型,那就递归

newObj[key] = obj[key].copyself();

}else{//基本类型,直接赋值

newObj[key] = obj[key];

}

}

return newObj;

}

var pNew = copyObj(p);

pNew.books[0] = "四国";

console.log(pNew);

console.log(p);

24面向对象的特点

封装

指利用抽象数据类型将数据和基于数据的操作封装在一起,成为一个独立实体,数据被保护在内部,尽可能隐藏内部细节,只保留一些对外接口与外部发生联系。

好处:封装之后成为独立实体,独立实体可以在不同环境复用,提高复用性,只提供了安全的操作接口,实体更加安全

继承

子类继承父类,使得子类拥有父类的属性,或者子类从父类继承方法。

好处:在不同的类可能有共同的特征和动作,可以把共同部分放到一个类中,让其它类共享。将一个通用类拓展为多个特定类,继承可以再共同部分的基础上拓展功能,避免重复,更易于维护和拓展,可以不修改其他代码,继承出新的特定类并实现新的方法。

多态

前提是已经有封装成的独立体,独立体之间存在继承关系。

多态是同一个行为具有多个不同表现形式或形态的能力。是同一个行为发生在不同对象会产生不同的效果。

多态存在的三个必要条件:继承、方法覆盖、父类型引用指向子类型对象。

public class Animal {

public void move(){

System.out.println("Animal move!");

}

}

public class Cat extends Animal{

//方法覆盖

public void move(){

System.out.println("I can jump");

}

//子类特有

public void catchMouse(){

System.out.println("I can catch mouse");

}

}

[点击并拖拽以移动]

public class Bird extends Animal{

//方法覆盖

public void move(){

System.out.println("I can fly");

}

//子类特有

public void catchBug(){

System.out.println("I can catch bugs");

}

}

25webpack

26继承的实现

原型链继承

首先new一个main()的实例,既然是实例那么--proto--就会指向main的原型对象,也就可以访问main挂载到main.prototype上的方法和属性,所以我们把personal的原型对象指向这个main实例对象,此时,personnal的实例对象的constructor被覆盖,所以手动把personnal原型对象的constuctor指向personal,重新建立联系。之后在personal的原型对象上添加属性和方法,此时person的实例对象p就可以既访问..又可以访问...

// 先继承

Personal.prototype = new Main();

Personal.prototype.constructor = Personal;

// 后定义属性和方法

Personal.prototype.name = 'hwk';

Personal.prototype.sayName = function () {

console.log('Personal name')

}

// 正确输出

console.log(p.sex ) ; // 男

console.log(p.name) ; // hwk

p.eat(); // Main eat ...

p.sayName (); // Personal name

构造函数继承

function SuperType(){

this.colors = ["red", "blue", "green"];

}

function SubType(){

//继承了SuperType

SuperType.call(this);

}

var instance1 = new SubType();

instance1.colors.push("black");

alert(instance1.colors); //"red,blue,green,black"

var instance2 = new SubType();

alert(instance2.colors); //"red,blue,green"

这里subtype构造函数的内部 调用了 supertype的call方法,意思是把父类中通过this指定的属性和方法复制到子类创建的实例中,,然后new的subtype就携带着父类中的属性和方法

组合继承

function SuperType(name){

this.name=name;

this.colors=["red","blue","green"];

}

SuperType.prototype.sayName=function(){

console.log(this.name);

}

function SubType(name,age){

SuperType.call(this,name);

this.age=age;

}

SubType.prototype=new SuperType();

SubType.prototype.constructor=SubType;

SubType.prototype.sayAge=function(){

console.log(this.age);

}

var instance1=new SubType("zxf",24);

instance1.colors.push("black");

console.log(instance1.colors);//["red","blue","green","black"]

instance1.sayName();//"zxf"

instance1.sayAge();//24

var instance2=new SubType("jay",36);

console.log(instance2.colors);//["red","blue","green"]

instance2.sayName();//"jay"

instance2.sayAge();//36

subtype通过构造函数继承的方式,也就是call,把父类的name属性和color属性拿了过来,

之后挂载到supertype上的sayname方法,通过subtype的原型对象,指向一个super实例,然后把构造器重新赋值一下,获得了sayname的方法,然后所有属性和方法就都被继承过来了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自私还是利己主义?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值