最近发现一个问题很好玩,来跟你们分享一下。javascript
var a = {};
funcutin b(a) {
a.b = 1;
}
b(a);
console.log(a);
复制代码
若是可以理解值传递和引用传递的同窗,就知道打印出来的就是 { b: 1};java
若是是下边这样的写法呢es6
var a = {};
function b(a) {
a = 1;
}
b(a);
console.log(a);
复制代码
此时打印出来是编程
若是改写成这样的:编程语言
var a = {};
function b(a) {
a.b = 1;
var a = 1;
}
b(a);
console.log(a);
复制代码
此时打印出来是函数
这样的呢:ui
var a = {};
function b(a) {
var a = 1;
a.b = 1;
}
b(a);
console.log(a);
复制代码
此时打印出来是lua
终极大招来临:es5
var a = { b: 1 };
function b(a) {
var a;
console.log(a.b);
a.b = 3;
a = { b: 2};
console.log(a.b);
}
b(a);
console.log(a.b);
复制代码
此时打印的是?spa
在 javascript 中数据类型能够分为两类:
原始数据类型值 Primitive type,好比Undefined,Null,Boolean,Number,String。
引用类型值,也就是对象类型 Object type,好比Object,Array,Function,Date等。
按值传递(call by value)是最经常使用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
按引用传递(call by reference)时,函数的形参接收实参的隐式引用,传递的是副本。这意味着函数形参的值若是被修改,实参也会被修改。同时二者指向相同的值。
通俗来讲就是值传递将整个变量都穿进去了,引用传递传入进去的是一个对象的副本(指针)
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另外一个地方的值。因为经过地址能找到所需的变量单元,能够说,地址指向该变量单元。所以,将地址形象化的称为“指针”。意思是经过它能找到以它为地址的内存单元。——百度百科
JS中保存基本类型的值和标识符在栈区,而引用类型的标识符和地址保存在栈区,值保存在堆内存中
简单举例
var a = 1;
var b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
var c = {};
var d = c;
c.name = '小周';
console.log(c); // { name: '小周' }
console.log(d); // { name: '小周' }
复制代码
接下来回到正题
首选咱们来看一段代码
var a = 1;
var a;
console.log(a); // 1
复制代码
这里第二行对a是一个重复宣告,而不是赋值,变量只有定义(宣告)后未赋值的状况下才会输出undefined,除非手动赋值undefined;那么这里,JS引擎对于重复宣告的规定以最近的变量指定(也就是赋值)做为变量在执行时的值,因此第二行的var a;其实至关于无效;
ps: 在咱们本身写代码中,通常不会作这样的蠢事。
js对形参在变量对象中是如何保存的呢,请看规范:
10.5 Declaration Binding Instantiation Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment‘s Environment Record. For function code, parameters are also added as bindings to that Environment Record.
意思就是: 不管是形参仍是函数中声明的变量,JS对他们的处理是没有区别的,都是保存在这个函数的变量对象中做为局部变量进行处理;结合下咱们刚才说的同名宣告,下边的题就能读懂了
function b(param) {
var param;
console.log(param); // 1
param = 2;
console.log(param); // 2
}
b(1);
复制代码
其实行参跟局部变量是同一个东西,都是保存在函数内部的变量。
回到咱们刚才终极大问题
var a = { b: 1 };
function b(a) {
var a;
console.log(a.b);
a.b = 3;
a = { b: 2};
console.log(a.b);
}
b(a);
console.log(a.b);
复制代码
总体分析一下
思考:
这次分享只是单纯的发现若是去深刻理解js,就会打开了一扇新世界的大门。随时欢迎你们来喝咱们一块儿互相交流,一块儿打开代码新世界的大门。
补充:
(function() {
var a = {}
function b(a) { // 函数声明的时候,叫作形参
// 形参等同于一次声明
a = 1;
}
b(a) // 此时的a是实参
console.log(a) // {}
})();
(function() {
//var a = 1;
//var a;
//==
//var a;
//var a;
//a = 1;
// 变量命名提高规则
// 当对一个变量进行重复声明的时候,默认以最后一次
// 有效声明为主
})();
(function () {
var a = {};
function b(a) {
var a
a.b = 1;
var a = 1;
console.log(a)
}
console.dir(b);
b(a);
console.log(a); // { b:1 }
})();
console.log('--------------------------');
(function() {
var a = {};
function b(a) {
var a = 1;
a.b = 1;
console.dir(a);
function c() {}
console.dir(c)
}
b(a);
console.log(a);
})()
var a = {}
// 对象的浅拷贝跟深拷贝
// 浅拷贝
// Object.assign(currentObj, targetObj)
// { ...currentObj, ...targetObj}
var b = {...a}
b.c = 1;
console.log(b, a)
// 浅拷贝的适用范围 对象的全部的值都只能是基础数据类型
// 深拷贝 _.clone()
复制代码
// 在es5当中 var 声明的是就是全局变量,global做用域等同于window对象
// 在es6中 let 和 const声明的在Script 做用域上,级别略低于全局做用域
// 这样就很好的解释了为何window.a获取不到,可是a却能获取到
// 在函数初始化的时候,会将本身内部全部权限访问到的做用域都挂载到[[Scope]]上
// 每一个函数都会有自身的[[Scope]]只读属性,这样的一个属性被咱们称之为做用域链
// 跟script标签有关系
let a = 1;
const b = 2;
var c = 3;
console.log('window.d', window.d)
function d() {
}
console.log('window.a', window.a)
console.log('window.b', window.b)
console.log('window.c', window.c)
console.dir(d)
复制代码