-
与某些语言把原始类型存储在栈中,把引用类型存储在堆中不同,JavaScript使用一个变量对象来追踪变量的生存周期:原始值直接保存在这个对象内,引用类型的指针(指向这个引用类型在内存中的地址)被保存在这个对象内。
-
由于上面的原因,当把一个引用类型赋值给一个变量时,只是把它的指针赋值给了这个变量。
-
解除对一个对象的引用的最好的方法是将对象变量赋值为
null,这样垃圾收集机制就能更好的处理无用的垃圾了。例如:let pointer = {}; // 创建一个引用了对象的变量 pointer, 称为对象变量 // do something with pointer pointer = null; // 将对象变量赋值为 null, 它引用的对象会被垃圾收集,那块内存就能空出来上面的代码用一个
pointer变量引用了一个对象,在将pointer变量设置为null之后,垃圾收集器就能更好的处理那个已经没有了引用的对象。 -
对于所有的引用类型,使用字面量形式创建的对象并没有调用构造函数,但是JavaScript引擎在背后做的工作和调用了构造函数时一样。例如对于一个普通对象:
// 使用对象字面量的方式创建一个对象 let person = { name: '王大锤', age: 30 }; /* 等价于下面使用构造函数创建的对象 */ // 使用构造函数创建一个对象 let person = new Object(); person.name = '王大锤'; person.age = 30;又例如对于一个数组:
let arr = [1, 2, 3]; // 使用字面量形式创建一个数组 /* 等价于下面使用构造函数的形式创建数组 */ let arr = new Array(1, 2, 3); -
使用
Array.isArray方法鉴定一个变量是不是数组,使用instanceof操作符也可以判断一个变量的值是不是数组的实例,但是如果变量在同一个网页的不同框架之间传递,由于每个框架都有自己的环境,所以后者可能无法得到准确的结果,但是前者始终能得到正确的结果。let arr = [1, 2, 3]; console.log(Array.isArray(arr), arr instanceof Array); // true true -
对原始类型的值使用
instanceof操作符判断其对应的类型,总会返回false,这是因为原始类型虽然有打包操作,但是在使用instanceof进行判断时,打包操作就已经结束了,此时打包出来的临时对象已经被销毁,所以结果为falselet name = 'Jack Ma'; let age = 40; let flag = false; console.log(name instanceof String); // false console.log(age instanceof Number); // false console.log(flag instanceof Boolean); // false -
函数存在一个被称为
[[call]]的内部属性,内部属性无法通过代码访问,这里使用双中括号来标注此类属性名。[[call]]属性是函数的独有属性,而且typeof操作符对具有[[call]]属性的对象返回function,这就是使用typeof判断函数类型的原理。 -
函数声明可以被提升是因为引擎提前知道了函数的名字;函数表达式是使用匿名函数定义的,变量名只是引用了这个匿名函数,虽然可以通过变量对函数进行调用,但变量名并不是函数的名字,所以无法进行提升。
-
sort方法在排序的时候会将对象转换成字符串然后再比较,所以在不指定比较函数的时候不能对纯数字数组进行准确排序。 -
函数也有
length属性,表示函数期望的参数个数,也就是函数声明的形参个数。 -
函数和方法的区别:其实这两个名称指的都是函数,只是当一个函数是一个对象的属性时,相对于这个对象,函数就被称为了方法。
-
当属性***第一次***被添加给对象时,JavaScript会调用对象名为
[[put]]的内部方法来创建这个属性,并赋值。 -
当属性被添加给对象之后,再改变属性的值时,不会再调用再调用
[[put]]方法了,这时会调用[[set]]这个内部属性。let obj = {}; //定义一个对象,这个对象没有任何用户自己创建的属性 obj.name = 'Jack M'; // 调用了 [[put]] 方法,因为 obj 本来没有 name 属性,这里给它添加了 name 属性 obj.name = 'Mask'; // 调用了 [[get]] 方法,因为这时已经存在了name属性,这里只是重新给属性赋值 -
如果想删除对象的某个属性,应使用
delete操作符。注意:直接将属性设置为null是无法删除这个属性的,这样只是给属性赋了一个新值为null:// 创建一个带有 name 属性的对象 let obj = { name: 'Yuri' }; // 将 name 属性值设为 null,无法删除这个属性,只是给它赋值为 null, 即 obj.name === null obj.name = null; console.log(obj.name === null); // true // 使用 delete 关键词可以真正删除属性 delete obj.name; console.log(obj.name); // undefined console.log('name' in obj); // false -
想遍历对象的属性时,可以用两种方法:
for ... in ...:迭代对象的可枚举的***属性名***,可枚举属性是指[[Enumerable]]值为true的属性Object.keys(object):这个方法返回对象的所有可枚举属性的属性名组成的数组。
// 创建一个带有两个自有属性的对象 let obj = { name: 'Yuri', age: 40, speak: 'You will obey ...' }; for(let propertyName in obj){ // 每次都会将 对象的属性名赋值给 propertyName console.log(propertyName); } // name // age // speak let allProperties = Object.keys(obj); // 获得 obj 对象的所有可枚举属性的属性名 console.log(allProperties); // ["name", "age", "speak"]注意:这两个方法取得的可枚举属性是有差别的,
for in方法会遍历对象的原型链,而Object.keys()方法只会涉及到对象本身的属性,不会访问原型链。 -
对象的大部分自带的属性的
[[Enumerable]]的值都是false,即不可遍历,用实例对象的propertyIsEnumerable方法可以判断一个属性是不是可枚举的:console.log(obj.propertyIsEnumerable('name')); // true console.log(obj.propertyIsEnumerable('age')); // true console.log(obj.propertyIsEnumerable('speak')); // true -
使用构造函数创建对象时,如果不需要传递参数时,可以不加小括号:
// 定义一个构造函数 function Person(name){ /* xxx */ } let p1 = new Person(); let p2 = new Person; // 不加小括号 console.log(p1 instanceof Person, p2 instanceof Person); // true true