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]"
   

以上内容如有不对,希望大家指出,谢谢。


雨夜望月
207 声望13 粉丝