序言
我们知道在JS中存在两种数据类型,一种是原始类型也可以称之为简单类型,另一种就是引用类型也可以称之为复杂类型,他们两个各自有什么特性,又有什么区别呢,接下来让我来逐步讲解,借助通俗易懂的代码,一起深入了解V8引擎是如何处理这两种数据类型的
原始类型 & 引用类型
原始类型的值是存在调用栈的
引用类型的值是存在堆当中的,但是会将引用地址存在栈中
原始类型
在JavaScript中原始数据类型有5种
数字(Number):用于表示数字,包括整数和浮点数。例如:5, 3.14。
字符串(String):用于表示文本数据,由一系列字符组成。字符串必须用引号(单引号或双引号)括起来。例如:"Hello, World!"。
布尔值(Boolean):用于表示逻辑值,只有两个可能的取值:true(真)和false(假)。布尔值用于条件判断和逻辑运算。例如:true, false。
空值(Null):用于表示一个空值或不存在的对象。它是一个特殊的关键字null。当变量被赋值为null时,表示该变量没有任何值。例如:var x = null;。
未定义(Undefined):用于表示未赋值的变量或不存在的属性。当变量被声明但未初始化时,其默认值为undefined。例如:var y;。
let a = 'hello'
let b = 123
let c = true
let d = undefined
let e = null
我们看这一段代码,首先想到的就是编译器首先会在调用栈中创建一个全局执行上下文并存入栈底,接着在全局执行上下文中的词法环境中添加变量abcde并且值为undefined,接着交给v8引擎分别给这些变量赋值。
let obj = {
name:'小花'
}
let lw = obj
obj.name = '小红'
console.log(lw.name);
接下来我们再看这段代码,如果这段代码出现在全局中,那么引擎和编译器会如何执行他们呢?
我们在上次文章认识了调用栈的内存是有限的,而且对象里面可以存放各种数据类型,比如对象、数组、字符串、函数等等,所以一个对象的内存上限是非常大的,于是在JS这门语言中,将会用一个堆来存放这些对象里的内容,堆里面每个属性都是由一个地址和值组成的,而在调用栈中对象的值会被赋值成这个地址,我们举个通俗易懂的例子,如果你有百万英镑,你第一步肯定是将这些钱存到银行中去,于是银行就会给你一张存折,当你需要取钱时,拿这张存折去对应的银行进行访问就可以了。这里我们的obj对象首先被存放在词法环境中,但是会将他在堆内的地址作为值赋值给他,这个地址也可以理解成指向堆中地址的一个指针。
在词法环境中还有一个lw对象,因为是把obj这个对象作为值赋值给他,但是v8引擎会把地址赋值给他,也相当于给了 lw对象一个指向堆中obj对象的指针,然后我们将obj.name改成了额'小红',首先引擎会识别到这是一个对象,于是按照他的地址寻找到堆,然后发现了obj对象中的属性,最后将obj.name改成了'小红' 最后输出lw对象中的name属性也就是相当于再顺着地址读取输出obj对象中的name属性 也就是输出‘小红’
对象
我们知道对象的一种创建方法,以及它的增删改查方法,我们先看下面这段代码学习一个向对象中新增属性的新方法。
let obj = {
name: '丁总',
age:18
}
// obj.girlFriend = '翠花' //增
// delete obj.girlFriend //删
//obj.age = 20 //改
// console.log(obj.name); //查
// console.log(obj['name']);
let girl = 'girlFriend'
obj[girl] = '小红'
console.log(obj);
我们创建了一个对象,并且这个对象有name和age属性,如果我们想要打印出这个对象name属性的值时,也可以用obj['name']获取这个属性的值然后输出
如果我们定义一个变量等于一个字符串'girlFriend',然后用上面的方法往对象里面增加属性也可以成功吗?执行结果为
对象的创建
var obj = {} //对象字面量|对象直接量
let obj = new Object() //构造函数
自定义构造函数
Object.create({})
首先我们来讲构造函数,构造函数就像是一个工厂,能够批量化生成对象,这些对象有一些相同的属性,也可以具有自己独特的属性。
构造函数
function Car(color){
this.name = 'BMW'
this.height = '1400'
this.lang = '4900'
this.weigth = 1000
this.color = color
}
let car1 = new Car('pink') //实例对象
let car2 = new Car('green')
console.log(car1);
console.log(car2);
这里我们定义了一个car的构造函数,这个函数拥有五个属性,当我们构造函数创建许多新对象时,默认这些对象刚开始就会被赋予这些属性,但是我们在构造函数里传入了一个新参color,这是用来我们实例化对象时可以传入一个值来更改color属性
上述代码运行的结果为
用new实例化对象时new的隐式过程
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
// new执行过程
// let this = {
// name:name,
// age:age,
// sex:sex
// }
// return this
}
其实当我们定义一个构造函数时,在构造函数引擎会隐式执行一个new方法,定义一个this对象,然后将传入进来的值赋给对应的属性,最后返回给this对象,所以呢,我们是不是可以验证一下,如果不用new但是我们自己在构造函数内创建一个new执行过程,话不多上上代码。
function Person(name, age){
var that = {}
that.name = name;
that.age = age;
return that
}
let p1 = Person('程总',18)
let p2 = Person('梓帆',19)
console.log(p1);
console.log(p2);
执行结果为:
我们看到这里的结果和上面用new方法创建对象的结果是一样的,其本质就是创建一个新的对象用来接收这些传入的属性值并返回。
用Object.create()创建对象
在 JavaScript 中,对象是基于原型继承的。每个对象都有一个原型,通过继承原型上的属性和方法来实现对象之间的共享和代码重用。
Object.create()
方法接受一个可选的参数,用作新对象的原型。传入的参数对象将成为新对象的原型,新对象将继承参数对象的属性和方法。
Object.create({})
是 JavaScript 中用于创建一个新对象的语法。它创建了一个空的对象,并将该对象的原型设置为传入的参数对象。
总结
原始类型,引用类型
-
原始类型的值是存在调用栈的
-
引用类型的值是存在堆当中的,但是会将引用地址存在栈中
对象
- 对象的创建方法
- var obj = {} //对象字面量|对象直接量
- let obj = new Object() //构造函数
- 自定义构造函数
- Object.create({})
- 构造函数就像工厂, 可以批量化生成对象
构造函数
- new 的隐式执行过程
-
创建一个this对象
-
执行函数中的this.xxx = xxx
-
隐式地返回this
感谢大家的阅读,点点赞吧♥
如果想了解更多有用的干货,点赞+收藏 编码不迷茫
开源Git仓库: gitee.com/cheng-bingw…