我们在写代码的时候为了提高代码的可读性和降低耦合性,经常需要将一大块逻辑代码拆解成小块。

但经常会遇到一个问题就是,有一个变量它贯穿于整个逻辑中,那么在拆分过程中他就需要被当成参数传入各个子模块。

这就涉及到一个问题,每个子模块对传入变量的操作有没有直接改变原变量的值呢?

下面我们来通过代码实现一下。

基本类型变量

NumberStringBoolean


// Number
var a = 111;
function changeNumber(param) {
	param = 222;
}
changeNumber(a);
console.log(a); // 111

// String
var b = "hello";
function changeString(param) {
	param = "world";
	param.concat(" world");
}
changeString(b);
console.log(b); // "hello"

// Boolean
var c = true;
function changeBoolean(param) {
	param = false;
}
changeBoolean(c);
console.log(c); // true

引用类型


// 对象
var a = {test: 1};
function changeObject(param) {
	param.test = 2;
}
changeObject(a);
console.log(a); // {test: 2}

但如果这里不是改变的对象属性,而是直接赋新的值呢?

var b = {test: 1};
function changeObject2(param) {
	param = {test: 2};
}
changeObject2(b);
console.log(b); // {test: 1}

注意对象里这两种的区别, 下面从编译器角度详细分析整个过程:

// 1. 开始处理var a, 编译器询问作用域是否有一个名叫a的变量,如果有的话,编译器忽略,继续向下走。
// 2. 结果是没有,则在当前作用域集合中声明一个新变量名叫a。
// 3. 编译器为引擎生成运行时的代码来处理a = {test: 1}。
// 4. 运行时会先询问作用域是否存在一个叫a的变量,如果是,引擎则会使用这个变量,否则的话会继续查找该变量。
// 5. 如果4最终未找到a,则抛出一个异常,这里显然已经找到了2中声明的a,所以申请一块内存,里面的值是{test: 1},并且将它赋值给a。
// 以上这个过程叫引擎为变量a做LHS查询(变量出现在赋值操作的左侧)。
var a = {test: 1};

// 6. 引擎在作用域中为changObject进行LHS查询。
function changeObject(param) {
	param.test = 2;
}

// 7. 引擎在作用域中为changeObject进行RHS引用,即“把changeObject的值找给引擎”
// 这个过程还隐含一个param = a操作,包含两步:一是对a的RHS引用,找到a的值,二是对param的LHS查询,即对param赋a的值。

// 注意,此时param和a共同指向了内存中的同一个对象{test: 2}。

// 如果是第一种param.test = 2;那么相当于对param.test这个变量做LHS查询,故param和a同时都改变了。
// 如果是第二种param = {test: 2};那么相当于对param这个变量做LHS查询,即断开param与原对象的指向,将其指向内存中新开辟的对象{test: 2}。原a指向原对象保持不变。
changeObject(a);

console.log(a); // {test: 2}

类似的,对于数组也是对象这种结果。

标签: javascript

添加新评论

0%