JavaScript这一系列文章,涉及到的点并不是很多,只是涉及到一些常见的如数据类型、闭包、原型链、属性描述符、ES6的module等,还有一些winter提出的一些问题。winter讲的JavaScript很深入,有些地方我不是很懂,就没有记录下来,大家如果有兴趣可以去看一下他的专栏。
这一篇文章主要讲的是JavaScript变量命名规则、数据类型、引用类型和基本类型的区别、类型检测的一些方法。
JavaScript变量
一、定义
变量: 变量就是一个用于存放数据的容器,它能够存储任何类型的数据
二、变量命名规则
1) 变量一般由字母、数字、下划线、$组成,不能以数字开头
2) 变量名不能使用JavaScript的保留字,如var、function、let、for等
3) 变量名应当遵循"小驼峰命名法",也就是小写整个命名的第一个字母,然后大写剩下单词的首字符
4) 变量名区分大小写,因此myAge 和 myage是两个不同的变量
5) 变量名不要以下划线开头,以下划线开头的被某些JavaScript设计为特殊的含义,可能让人迷惑
6) 变量名应当直观,它能描述所包含的数据
三、声明一个变量
/* var会产生变量声明提升,在变量声明前也可以使用,重复声明同一个变量名的变量时,后面的会覆盖前面的,可以使用window.variable来获取 */
var a = 1;
/* let、const其声明的变量只在其声明的块或子块中可用,会有一个暂时性死区,在let、const声明变量之前,这些变量都是不可用的;而且不能重复声明同一个变量名的变量。不能使用window.variable来获取。IE11支持let */
let b = 2;
const c = 3;
/* 不使用关键字直接创建变量,无论是在函数里还是外面,它都会成为一个全局变量;不会产生变量提升,可以使用window.variable来获取,可以使用 delete 来删除;重复声明同一个变量名的变量时,后面的会覆盖前面的 */
d = 4;
参考链接:
MDN--如何存储你需要的信息 — 变量
MDN--let
JavaScript类型
一、定义
基本类型:
1) Undefined: Undefined类型只有一个值,即undefined。它是一个变量,而非一个关键字。在使用var、let声明变量但未对其加以初始化时,这个值就是undefined。当一个变量没有声明时使用typeof,返回的结果也是"undefined"(var message; console.log(message === undefined) // true
)
2) Null: Null类型只有一个值,即null。从逻辑角度来看,null值表示一个空对象指针,这也是正是使用typeof操作符检测null值时会返回'object'的原因。用于定义一个将来用于保存对象的变量,可以将其值设置为null。
3) Boolean: Boolean类型只有两个字面值,true 和 false。
4) String: String类型用于表示由零或多个 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号(")或单引号(')表示。String 的意义并非“字符串”,而是字符串的 UTF16 编码,我们字符串的操作 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码。
5) Number: Number类型使用 IEEE754 格式来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。浮点数也就是数值中必须包含一个小数点,且小数点后面必须至少有一位数字。由于保存浮点数值的内存空间是保存整数值的两倍,所以ECMAScript会不失时机地将浮点数值转换为整数值(var a = 1.0; console.log(a); // 1
)。Number中有几种额外情况,NaN(转化为Number失败,且NaN 不等于NaN, 无论是 == 还是 ===)、Infinity(无穷大)、-Infinity(负无穷大)。
6) Symbol: 表示独一无二的值。可以用于对象的属性名,保证每一个属性的名字都是独一无二的值,从根本上防止属性名冲突。Symbol值通过Symbol函数生成。(var a = Symbol(1); var b = Symbol(1);a === b; // false
)
引用类型
Object: ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要一个构造函数(构造函数本身就是一个函数,只不过该函数时出于创建新对象的目的而定义的)来创建或者是通过对象字面量的方式进行创建。(var obj = new Object();var arr = new Array; var obj1 = [];
)在ECMAScript中,如果不给构造函数传递参数,则可以省略后面的那一对圆括号,但是不推荐。
注:
1)null与undefined之间使用相等操作符(==)时总是返回true。
2)以上除Symbole来自于 ES6入门 外,别的都是来自于 JavaScript高级程序设计(第3版)
基本类型和引用类型
一、区别
1) 基本类型将内容直接存储在栈中(大小固定位置连续的存储空间),记录的是该数据类型的值,即直接访问,基本类型赋值是复制;引用类型将内容存储在堆中,堆所对应的栈中记录的是指针(堆的地址),外部访问时先引出地址,再通过地址去找到值所存放的位置。引用类型赋值是地址引用。指向基本数据类型的变量相当于包含了数据,而指向非基本类型数据(引用类型)的变量本身是不包含数据的,它只是记录了一个内存地址。
2) 基本类型: 如果将一个基本类型使用=将一个变量的值赋值到另外的变量,实际上是将对应的值拷贝了一份,然后赋值给新的变量,是值传递,其中一个变量的改变不会影响另外一个。 (let x = 1; let a = x;
)
引用类型: 如果一个变量绑定到一个非基本数据类型(Array、Function、Object),那么它只记录了一个内存地址,该地址存放了具体的数据。如果将一个引用类型的变量赋值给另外一个变量,实际上是将内存地址复制了一份,其中一个变量的值发生改变时,另外一个也会改变。对象是通过引用传递,而不是值传递,引用类型的变量赋值只会将地址传递过去。如果将一个已经赋值的变量重新赋值,那么它将包含新的数据或者引用地址。如果一个对象没有被任何变量指向,JavaScript引擎的垃圾回收机制会将该对象销毁并释放内存。 (let x = {a: 1}; let b = x; x.a = 2; b.a === 2 //true
)
二、相关扩展
1.在 == 和 === 中,引用类型和基本类型是如何进行比较的?
==和===,对于引用类型的变量,==和===只会判断引用的地址是否相同,而不会判断对象具体里属性以及值是否相同。如果两个变量指向相同的对象,则返回true。如果是基本类型,==则会在两个变量类型不一样时进行隐式转换后则判断值是否相同,如果变量类型一样则会直接判断值是否相同;===则会判断两个变量的类型、值是否完全相同。
2.JS中的可变与不可变
基本类型是不可变的,如ECMAScript中的字符串是不可变的,也就是说字符串一旦创建,他们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后用另一个包含新值的字符串填充该变量;引用类型是可变的。
const str = '12';
str = '123'//Uncaught TypeError: Assignment to constant variable
const obj = {a: 1}
obj.a = 2;
console.log(obj) // {a: 2}
3.函数的参数都是按值传递的
ECMAScript 中所有函数的参数都是按值传递的,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。
向函数传递参数:
1) 参数为基本类型时: 被传递的值会被复制给一个局部变量。
2) 参数为引用类型时: 将一个对象当做参数传入函数内部后,即使是按值传递的,函数内部变量也会按引用来访问同一个对象。于是,当函数内部为这个变量添加属性时,函数外部的对象也会有所反映。因为 person 指向的对象在堆内存中只有一个,而且是全局对象。有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。但是如果在函数中在重新定义一个同名对象,重新设置其name属性。通过调用函数后,访问person.name时,显示的值依然是'Nicholas'。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写 obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
function setName (obj) {
obj.name = 'Nicholas';
obj = new Object();
obj.name = 'Greg';
}
var person = new Object();
setName(person);
console.log(person.name); // Nicholas
我的理解:
1) 这里很绕,理解这个的关键在于什么是引用传值。function addTen(num){num +=10; return num;} var count = 20; addTen(count);
在这个例子中,如果是按引用传值的话,那么当函数调用之后,count的值应当为30。也就是引用传值就是当函数里的变量发生变化时,全局变量也会进行改变,从而反映函数内部的修改。(还是看书不仔细,没有好好看,这个总结也是书里说的...)
2) 当函数带有参数时,会在函数体中进行一个隐式声明了一个局部变量,然后在将函数参数中的变量的值赋值给这个局部变量。当函数参数为对象时,会将原来的内存地址复制一份给局部变量,当其局部变量进行改变时,改变的就是全局的对象,因为地址一样。当将局部变量重新赋值一个新的对象时,会更换内存地址,这样一来两者就不会有什么关系。(let a = {name: 1}; let b={name: 1}
)就像a、b一样,虽然值一样但是内存地址不同,所以一个改变,另外一个并不会随之改变。
3)参数传递的策略:
a.按值传递: 参数的值是调用者传递的对象值的拷贝(copy of value),函数内部改变参数的值不会影响到外面的对象(该参数在外面的值),一般来说,是重新分配了新内存(我们不关注分配内存是怎么实现的——也是是栈也许是动态内存分配),该新内存块的值是外部对象的拷贝,并且它的值是用到函数内部的。
b.按引用传递: 按引用传递接收的不是值拷贝,而是对象的隐式引用,如该对象在外部的直接引用地址。函数内部对参数的任何改变都是影响该对象在函数外部的值,因为两者引用的是同一个对象,也就是说:这时候参数就相当于外部对象的一个别名。
c.按共享传递(Call by sharing): 函数接收的是对象对于的拷贝(副本),该引用拷贝和形参以及其值相关联。这里出现的引用,我们不能称之为“按引用传递”,因为函数接收的参数不是直接的对象别名,而是该引用地址的拷贝。最重要的区别就是:##函数内部给参数重新赋新值不会影响到外部的对象##,但是因为该参数是一个地址拷贝,所以在外面访问和里面访问的都是同一个对象(例如外部的该对象不是像按值传递一样完全的拷贝),改变该参数对象的属性值将会影响到外部的对象。
按共享传递这个策略很很多语言里都使用了:Java, ECMAScript, Python, Ruby, Visual Basic等。此外,Python社区已经使用了这个术语,至于其他语言也可以用这个术语,因为其他的名称往往会让大家感觉到混乱。大多数情况下,例如在Java,ECMAScript或Visual Basic中,这一策略也称之为按值传递——意味着:特殊值——引用拷贝(副本)。
参考内容:
来自JavaScript高级程序设计(第3版) 4.1.3 传递参数
知乎--JavaScript中函数都是值传递吗?
深入理解JavaScript系列(19):求值策略(Evaluation strategy)
类型检测
一、typeof
typeof: 使用typeof操作符返回一个字符串,表示未经计算的操作数的类型。使用typeof操作符的结果是一共有7种字符串。
typeof的返回结果:
- 'undefined': 如果这个值未定义
var name = void(0); typeof name; // "undefined"
- 'boolean': 如果这个值为布尔值
var flag = true; typeof flag; //"boolean"
- 'string': 如果这个值为字符串
var str = '1'; typeof str; // "string"
- 'number': 如果这个值为数值
var num = 1; typeof num; // "number"
- 'symbol': 如果这个值为symbol
var key = Symbol(1); typeof key; // "symbol"
- 'object': 如果这个值为对象或null
var obj = {}; var date = new Date(); var arr = {}; var reg = /1/; typeof obj; typeof date; typeof arr; typeof reg; // "object"
- 'function': 如果这个值为函数
function handle() {}; typeof handle; // "function"
注: typeof操作符对未初始化和未声明的变量执行typeof操作符都返回undefined值,但是是在没有使用let、const声明变量的情况下。如果先使用typeof检测一个变量,然后再用 let、const声明这个变量,会报错。
二、Object.prototype.toString.call()
Object.prototype.toString.call(): 返回一个表示该对象的字符串。
Object.prototype.toString.call()的返回结果:
Object.prototype.toString.call('字符串'); // "[object String]"
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)); // "[object Symbol]"
Object.prototype.toString.call(/1/); // "[object RegExp]"
Object.prototype.toString.call(function() {}); // "[object Function]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(new Date()); // "[object Date]"
以上内容如有不对,希望大家指出,谢谢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。