数据类型,原型原型链,继承

  1. js的变量、作用域、闭包、this、原型及原型链
  2. js的事件循环
  3. es6+新特性

js数据类型

js数据类型分为基本数据类型和引用数据类型两种,分别是:
1 基本数据类型
string(字符串)
boolean(布尔)
number(数字)
undefined()
null
symbol
2 引用类型
Object
Array
Function

两种数据类型有什么区别?

1 存放位置不同,
基本类型的变量会保存在栈内存中,如果一个函数中声明一个基本类型的变量,这个变量在函数执行结束后会自带销毁
而引用类型的变量名会保存在栈内存中,但是变量值会保存在堆内存中,引用类型的变量不会自动销毁,当没有引用变量引用它时,
系统的垃圾回收机制会回收它。
赋值不同
基本类型的赋值相当于深拷贝,复制后相当于又开辟了一个内存空间

let a=100;
let b = a;
b = 101;
console.log(a,b);//100 101

可以看出a的值改变时不影响b的值

let a = 100;
let b = a;
b = 101

而引用类型的赋值是浅拷贝,当我们对对象进行操作时,
其实操作的只是对象的引用

let obj = {
	name:"蓝桥",
};// 创建一个obj对象
let obj2 = obj; //obj赋值给obj2

obj2.name = "蓝桥云课";

console.log(obj.name,obj2.name) // obj2指向的内容变了,obj也变了

可以看出,我们把obj的值赋给obj2,当obj2的值改变时,obj的值也被改变

如何实现一个对象的深拷贝呢?

1 使用JSON.stringfy 和JSON。parse这组API
JSON.stringfy()用于将js值转换为JSON字符串;
JSON.parse() 方法将JSON字符串转化为js对象

JSON.stringfyJSON.parse()
将js值转换为JSON字符串将JSON字符串转化为js对象
let obj = {
	name:"蓝桥"
};//创建一个obj对象

let obj2 = JSON.parse(JSON.stringify(obj));
obj2.name = "蓝桥云课"

console.log(obj.name,obj2.name) //蓝桥 蓝桥云课

但是这种方式有些问题,当obj里面有函数或者undefind就会失效

let obj = {
	fn:function () {
		console.log("我是蓝桥")
},
name:undefined
};
let obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2) //{}


所以完全之策还是要用递归,如下所示

function deep(obj) {
	let oo = {};
	for(const key in obj ) { 
		if ( typeof obj[key] === " object") {
			oo[key] = deep(obj[key]);
	}else {
			oo[key] = obj[key];
			}
		}
		return oo;
}

上述代码的原理就是递归遍历对象的每个属性,分别复制到一个新对象去
第2遍

function deep(obj) {
	let oo = {};
	for(count key in obj) {
		if(typeof obj[key] ==="object") {
			oo[key] = deep(obj[key]);
		}else {
			oo[key] = obj[key];
		}
	}
	return oo;
}

有经验的面试官会从一道题目上层层递进的提问,来区分不同面试者的水平

原型及原型链

面试中经常问道,你知道什么是原型吗?或者你对原型链是如何理解的,这道面试题挺难的,要想彻底搞清楚原型,及原型链
所有的知识点都来自于一下理论
每个函数都有prototype属性,每一个对象都有_proto_属性,这个属性称之为原型,在我们执行new的时候,对象的_proto_指向这个构造函数的prototype
首先,每个函数都有一个prototype属性,这个属性指向函数的原型对象,同时prototype里面有个constructor属性会指道该函数

function Demo() {
Demo.prototype.constructor === Demo; // true

Demo.prototype结构如下所示

Demo.prototype
{constructor:f}
	constructor:f Demo()
		arguments: null
		length:0
		name:"Demo"
		prototype:{constructor:f}
		_proto_:f()
		[[functionLocation]]:
		[[Scopes]]:Scopes[1]
	_proto_:Object
		

现在我们使用new操作符来创建一个实例对象,如下所示
当使用new操作符后,Demo就变成了构造函数

function Demo() {
const d = new Demo(); //d就是创建出来的实例对象

d既然是对象,自然就有proto,同时指向构造函数Demo的prototype

function Demo() {
	this.name = " 蓝桥";
	}
Demo.prototype.say = function () {
console.log("我是",this.name);
};
const d = new Demo();
// 虽然Demo 上没有say方法,但是Demo的prototype上有此方法,所以下面的调用可以正常打印
d.say() 

用一张图来描述这个查找过程

d.say()
d中查找say方法
发现d中没有
去d的_proto_中查找
d.proto === Demo.prototype
等价于去Demo.prototype查找
Demo.prototype中找到say方法
调用say方法

这里我们只是体现出来一层查找,实际上__proto是逐层向上查找的,这个查找的过程也就是我们所说的原型链

如果面试中遇到面试官,只要记住上涨途中的查找流程然后描述给面试官即可

继承

js是如何实现继承的?在ES6之前,我们都是通过原型的方式来是按,这也是原型的重要使用场景之一,通常由如下集中实现方法:
1 原型赋值

function Per(name) {
	this.name = name || "蓝桥";
}
Per.prototype.say = function () {
	console.log(this.name);
};
function Sun() {
	Sun.prototype = new Per() ; //直接new了一个父类的实例,然后赋给了子类的原型
	Sun.prototype.love = function () {
	console.log(this.name);
	};
	const sun = new Sun();
	console.log(sun.name)

这种方法是直接new了一个父类的实例,然后赋给子类的原型。这样也就相当于直接将父类原型中的方法属性以及挂载this上的各种方法属性全赋给了子类的原型,简单粗暴!

因为简单,所以弊端,问题
1 创建子类实例时,无法向父类构造函数传参
2 来自原型对象的所有属性被所有实例共享

2构造函数
直接new 一个父类赋值给子类不合适,那我们在子类的prototype通过改变this指向,调用父类也可以达到继承的目的

function Per(name) {
	this.name = name || "蓝桥";
}
Per.prototype.say = function () {
	console.log(this.name);
};
function Sun() {
		Per.apply(this,arguments); //通过apply,调用Pre,海边Pre的this指向
}
const sun = new Sun();
console.log(sun.name)//蓝桥

这种方式不能改变prototype的this指向,所以不能继承父类的prototype

sun.say() //报错

3 组合继承
既然前两者由自己的优缺点,那组合起来实现继承吗?

在这里插入代码片

省略很多

面试官问:js如何实现继承的
1 原型继承,创建一个父类直接赋值给子类的prototype
缺点:父类的属性会被所示的子类共享,不能给父类传参
2 构造函数继承,在子类中通过改变this的指向来调用父类
缺点:不能继承父类的prototype
3 组合继承,结合原型继承和构造函数继承的有点
缺点:子类继承了两份父类,重复继承
4 寄生组合继承,通过Object.create() 方式来优化组合继承
缺点:实现起来比较复杂
5 ES6 extends实现继承
缺点: 低版本浏览器存在兼容性问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值