1.值类型 (5种): 栈中存储的是数据,赋值时拷贝的也是数据。修改拷贝后的数据对原数据没有影响。
2.引用类型(2种) : 栈中存储的是地址,数据在堆中,赋值时拷贝的也是地址。修改拷贝后的数据对原数据有影响的。
<script>
//1.值类型
var num1 = 10;
var num2 = num1;
num2 = 20;
console.log(num2);//20
console.log(num1);//10
//2.引用类型
var arr1 = [10,20,30];
arr2 = arr1;
arr2[0] = 100;
console.log(arr2);//[100,20,30]
console.log(arr1);//[100,20,30]
</script>
ECMAScript中所有的函数的参数都是按值传递的,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本基本类型变量的复制一样,而引用类型值的传递,如同引用类型变量的复制一样。有不少开发人员会在这一点上感到困惑,因为访问变量有按值和按引用两种方式,而参数只能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(arguments对象中的一个元素),在向参数传递引用数据类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部 --《JavaScript高级程序设计》以上都是书中原话
在读到《JavaScript高级程序设计》的4.1.3参数传递这一章的时候十分困惑,书中例举了几个案例来说明传递参数都是按值传递的
function addTen(num){
num+=10;
return num;
}
var count = 20;
var result = addTen(count);
console.log(count)//20 没有变化
console.log(result)//30
以上这个案例传递的是基本数据类型很好理解,num传递的是值不会影响函数外部变量count,他们仅仅具有相同的值。
但是我们接着使用对象,那就不那么好理解了:
function setName(obj){
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
console.log(person.name);//“Nicholas”
以上代码创建了一个对象保存在了变量person中,然后将对象传入setName()函数中之后就被复制给了obj,在这个函数内部,obj和person引用的是同一个对象,换句话说,即使这个变量是按值传递的,obj也会按照引用来访问同一个对象。于是当在函数内部修改了obj的name属性后,函数外部的person也将有所反映;到这里很多人会认为在局部作用域中修改的对象会在全局作用域中反应出来,说明参数是按引用传递的,至此书中又举了一个经过修改后的例子:
function setName(obj){
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name);//“Nicholas”
这个例子在吧person传递给setName()后其name属性被设置为Nicholas然后又将一个新对象赋值给了obj,同时将其name改为了“Greg”,如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为“Greg”的新对象,但是接下来访问的时候person.name依旧是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持不变。实际上,当函数内部重写obj时,这个变量引用的就是一个局部对象了,而这个局部对象会在函数执行完毕后立即被销毁。
以上就是《JavaScript高级程序设计》对函数传递参数的说明,但是还是很难理解向参数传递引用类型的值时是按值传递的的。这里我的理解是函数的参数都是按值传递的,传Object
类型也是一样,只不过这个值是地址值
,也就是说函数的参数传递的都是栈空间中的值(值类型的值就是数据,引用类型的值就是地址)上面的代码我是这么理解的:
我们把person的地址值传递给obj后,obj和person都指向了堆内存中的同一块地址(假设是a地址)
,所以这里对obj进行添加或者删除属性的操作,都是在a地址
上操作的,所以相应的变化会反映在person对象上。
那么,当对obj重新赋值(新的地址b)
,这个时候就切断了obj和a地址之间的联系,所以对obj上的任何操作也不会反映到person对象上。如果函数的参数是按引用传递的话,person也会指向地址b,然而事实上person指向的是地址a
所以:引用类型传递,传递的是地址的值,修改成员对象会影响原对象,但对变量重新赋值,不会影响原对象
<script>
//1.for-in循环遍历对象属性
var person = {
name: '张三',
age: 38
};
for (var key in person) {
console.log(key);//获取对象属性名字符串
console.log(person[key]);
};
//2.判断对象是否 包含 某个属性
var student = {
name: '班长',
age: 38,
sex: '男'
};
console.log("name" in student)//true
console.log("age" in student)//true
console.log("sex" in student)//true
console.log("girlFriend" in student)//false
//3.判断数组是否 包含 某个下标
var arr = [10, 20, 30, 40, 50];
console.log(5 in arr);//false
console.log(4 in arr);//true
console.log(0 in arr);//true
//如何判断数组中是否包含某个元素
console.log(arr.indexOf(10));//0 如果有则返回该元素下标
console.log(arr.indexOf(100));//-1 如果没有则返回固定值 -1
</script>
<script>
//delete关键字两个作用
//1.删除对象的属性 : delete 对象名.属性名
/*对象的属性既可以动态添加,也可以动态删除*/
var person = {
name: '班长',
sex: '男'
}
person.girlFriend = '苍老师';//动态添加属性
delete person.sex;//动态删除属性
console.log(person);
//2.删除没有使用var声明的变量 ( 有var声明的delete删除无效 )
num = 10;
delete num;
// console.log ( num );//程序报错num is not defined
</script>
1. === : 全等运算符(不存在类型转换)
// 严格匹配 : 数据类型 与 值 必须要同时相等
console.log('1' === 1 );//false
console.log( 1 === 1 );//true
console.log( undefined === null );//false
2. == : 比较运算符 : 隐式转换是有前提条件的 ( x == y )
2.1 x和y 都为 null
或者undefined
// 不会类型转换,固定返回true
console.log( Number(null) );//0
console.log( Number(undefined) );//NaN
console.log( null == null );//true
console.log( undefined == undefined );//true
console.log( null == undefined );//true
2.2 x或y 为NaN
// 不会类型转换,固定返回false (NaN与任何数据都不等)
console.log( NaN == 0 );//false
console.log( NaN == undefined );//false
console.log( NaN == null );//false
console.log( NaN == NaN );//false
2.3 当x或y 都为 :string,number,boolean
。 且数据类型不一致,此时会转换成number后运算
console.log( 1 == '1' );//true 1 == Number('1')
console.log( false == '0' );//true Number(false) == Number('0') 0 == 0
2.4 x或y 存在引用类型,则会转成 `原始值` 后比较
// 一个是引用类型,一个是基本类型
// 引用类型转原始值 : 先调用对象的valueOf() 方法获取原始值,然后调用toString() 转成字符串比较
// a : valueOf() : 可以忽略, 对象类型默认valueOf会返回自身
console.log( [10,20,30].valueOf() );// [10,20,30]
console.log( new Number(10) );//10 valueOf() 默认只会对基本包装类型有效
// b1. toString() : 数组的toString() 本质是调用join()转成字符串
console.log( [10,20,30] == '10,20,30' );//true
console.log( [] == '' );//true
console.log( [] == 0 );//true (1) [].toString (2) '' == 0 (3) 0 == 0
// b2. toString() : 对象的toString() 返回固定格式字符串 '[object Object]'
console.log( {name:'张三'} == '[object Object]');//true
console.log( {name:'李四'} == '[object Object]');//true
以下是针对2.4的面试题的坑
console.log( [] == 0 );//true
//这里我们要了解其他类型转Boolean为false的几种情况:0、-0、null、undefined、false、NaN、""
//(1) ![] 隐式规则转布尔类型 !Boolean([]) = !true = false
//(2) flase == 0
//(3) Number(false) == 0 0 == 0
console.log( ![] == 0 );//true
console.log( {} == 0 );//false '[object Object]' == 0
console.log( !{} == 0 );//true 原理同 ![] == 0 不存在调用toString() 本质是逻辑非表达式结果 与 0 比较
2.5 x或y 都为引用类型。则直接比较两者地址,地址一致则为true,地址不一致则为false
var arr1 = [10,20,30];
var arr2 = arr1;//拷贝地址 赋值
var arr3 = [10,20,30];
//虽然arr1 arr2 arr3打印的时候,一模一样的。但是他们对应的堆地址不同(钱一样银行卡不一样 )
console.log(arr1 == arr2);//true
console.log(arr1 == arr3);//false
console.log(arr2 == arr3);//false
以下是针对2.5的面试题的坑
console.log( [] == [] );//false 声明两个空数组,堆中地址不同
//(1)左边 ![] = !true = false
//(2)右边 [].toString = ''
//(3)false (数字0) == ''(数字0)
console.log( ![] == [] );//true
console.log( ![] == ![] );//true 两个逻辑非表达式比较 !true == !true false == false
//原理同上
console.log( {} == {} );//false
//(1)左边 !{} = !true = false
//(2)右边 {}.toString = '[object Object]'
//(3) false (数字0) == '[object Object]' (数字NaN)
console.log( !{} == {} );//false
console.log( !{} == !{} );//true
以下为特殊情况
// null是原型链终点, 底层是堆地址起点 0x0000
console.log( null == 0);//false null不会转换 ,会直接用地址 和 0 比较
//以上五种情况 只是对 == 运算符生效。 其他的比较运算符还是转number
console.log( null >= 0);//true Number(null) >= 0 0 >= 0
console.log( null <= 0);//true Number(null) <= 0 0 <= 0
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。