js的变量有着松散类型的本质,就是变量存储的值及数据类型可以在脚本的生命周期里改变。
这个特性是既强大又容易出问题。 (这也是为啥现在更加推崇使用ts来编写项目)
基本类型和引用类型的值
偶然看到群里有人发了一个链接,点进去一看https://www.cnblogs.com/youxin/p/3354903.html
什么?没有按引用传递? 先不说这篇文章的博主写的对不对,我先来自己捋一捋这块的概念。
小红书在第四章讲到,ECMAscript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。
基本类型的值就是简单的数据段,前面提到过的,string null undifined number blooean Symbol都是基本类型。
那自然不用说,其他的就是引用类型了,也就是我们常说的对象。
基本类型可以直接操作保存在变量中的值。按值访问。
引用类型的值是保存在内存中的对象。这块有个重点, js与其他语言不同,不允许直接访问内存中的位置。 也就是不能操作对象的内存空间。 操作对象时候,实际上是在操作对象的引用,而不是实际的对象。
书下附了一段字
这种说法不严密,复制保存着对象的变量时,操作的是对象的引用,但是在为对象添加属性时,操作的是实际的对象。
原理:我的理解
基本数据类型都是放在栈内存中的,拷贝的话会新开辟一个新的内存空间,然后把原始值复制一份的新的内存中,两部分是独立的。
(图解准备中)
而引用数据类型就不同了。
创建引用数据类型时候,会将引用类型存放到堆内存中。 前面讲到了,我们不能直接访问引用类型。
js会在栈中存放一个指针,这个指针指向堆内存中的引用类型数据。
而直接进行复制的话其实复制的是指针。 这个指针存储的就是引用类型数据的地址。
函数传递参数
函数传递参数其实只有值传递, 基本类型传递就相当于基本类型的值复制了一份, 引用类型传递同引用类型复制一样,其实传递的是栈中存储的指针。
注意: 如果直接在函数中对传入的引用类型重新赋值,相当于把指针的副本指向了一个新的对象,这样是不会改变原对象的。 如果是添加属性的话,则是通过指针找到堆中存储的对象进行操作,这样会影响对象本身。
类型检查
前面说的typeof可以检查出基本数据类型,但是对于引用类型,无法进行判断。 不能知道是哪一个对象类型
检测类型 instanceof 构造函数
判断是不是某一个类型。
那它的原理是什么呢?
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {
if (L === null)
return false;
if (O === L) // 当 O 显式原型 严格等于 L隐式原型 时,返回true
return true;
L = L.__proto__;
}
}
其实就是顺着原型链不断的向上查找,如果检测类型的原型链上包含构造函数的原型,那么就是true
instanceof 从原型的角度,来判断某引用属于哪个构造函数,从而判定它的数据类型。
执行环境及作用域
执行环境定义了变量或者函数有权访问其他数据,决定了它们各自的行为。每个执行环境都有与之关联的变量对象。
环境中定义的所有变量和函数都在这个对象中。
全局环境是最外围的一个执行环境。 在web浏览器中表现为window对象。