语言基础 - 1(未完整)

1. 语法

ECMAScript的语法很大程度上借鉴了C语言和其他类C语言。

1.1 区分大小写

ECMAScript中的一切都区分大小写

1.2 标识符

所谓标识符,就是变量、函数、属性或函数参数的名称。

  • 第一个字符必须是字母、下划线_或美元符号$(不以数字开头)
  • 剩下的其他字符可以是字母、下划线_、美元符号$或数字

按照惯例,ECMAScript标识符使用驼峰大小写形式,即第一个单词的首字母小写,后面每个单词的首字母大写。

(关键字、保留字、true、false 和 null 不能作为标识符。)

1.3 注释

// 单行注释

/* 多行注释
	  多行 */

1.4 严格模式

ECMAScript5增加了严格模式的概念。严格模式是一种不用的js解析和执行模型,ECMAScript3的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误。

要对整个脚本启用严格模式,在脚本开头加上这一行:

"user strict";

也可以单独指定一个函数在严格模式下执行,只要报这个预处理指令放到函数体开头即可:

function doSomething(){
	"user strict";
    // 函数体
}

1.5 语句

ECMAScript中语句以分号;结尾。省略分号意味着由解析器确定语句在哪里结尾。

代码块有一个左花括号{标识开始,一个右花括号}标识结束。在控制语句中使用代码块可以让内容更清晰,在需要修改代码时也可以减少出错的可能性。

2. 关键字和保留字

关键字: break case catch class const continue debugger default delete do else export extends finally for function if import in in stanceof new return superswitch this throw try typeof var void while with yield

保留字:

  • 始终保留: enum
  • 严格模式下保留:implement interface let package protected private public static
  • 模块代码中保留:await

3. 变量

ECMAScript变量是松散类型的,意思是变量可以用于保存任何类型的数据(类型可变)。每个变量只不过是一个用于保存任意值的命名占位符。

3.1 var关键字

不初始化的情况下,变量会保存一个特殊值undefined

①var声明作用域:
使用var操作符定义的变量会成为包含它的函数的局部变量。使用var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁。但在函数内定义变量时省略var操作符,可以创建一个全局变量。
(在严格模式下,不允许变量不声明,且不能定义名为eval和arguments的变量,否则会导致语法错误)

②var声明提升:
使用var这个关键字声明的变量会自动提升到函数作用域顶部(只提升声明,不提升赋值)。所谓的“提升”就是把所有变量声明都拉到函数作用域的顶部。因此,反复使用var声明同一个变量是没有问题的

3.2 let声明

let声明的范围是块作用域,而var声明的范围是函数作用域。

if(true){
    var name = "hsy";
    console.log(name); // hsy
}
console.log(name); // hsy
if(true){
	let age = 22;
    console.log(age); // 22
}
console.log(age); // ReferenceError: age没有定义

age变量之所以不能在if块外部被引用,是因为它的作用域仅限于该块内部。块作用域是函数作用域的子集,因此适用于var的作用域限制同样也适用于let。

let不允许同一个块作用域中出现冗余声明,即在同一块作用域中不能用let重复声明同名变量。对声明冗余报错不会因为混用let和var而受影响:

var name;
let name; // SyntaxError

let age;
var age; // SyntaxError

①暂时性死区: let声明不会在作用域中被提升。在let声明之前的执行瞬间被称为“暂时性死区”,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。

②全局声明: 与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性。

var name = "hsy";
console.log(window.name); // hsy

let age = 22;
console.log(window.age); // undefined

③条件声明: 不能使用let进行条件式声明,因为无法知道前面是否已经声明过同名变量,容易报错。

④for循环中的let声明:

  for (var i = 0; i < 5; i++) {
    // 循环逻辑
  }
  console.log(i); // 5 
  	
  for (let n = 0; n < 5; n++) {
    // 循环逻辑
  }
  console.log(n); // ReferenceError: n没有定义
  for (var i = 0; i < 5; i++) {
    setTimeout(() => console.log(i), 0)
  }
  // 实际输出 5 5 5 5 5
  for (let n = 0; n < 5; n++) {
    setTimeout(() => console.log(n), 0)
  }
  // 实际输出 0 1 2 3 4

3.3 const声明

主要用于声明常量,用它声明时必须同时初始化,且尝试修改const声明的变量会导致运行时的报错。

  • const同let一样不允许重复声明
  • const同let一样作用域为块
  • 如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制

3.4 声明风格及最佳实践

  • 不使用var
  • const优先,let次之

4. 数据类型

ECMAScript有6种简单数据类型:UndefinedNullBooleanNumberString、和 Symbol;一种复杂数据类型:Object

4.1 typeof操作符

返回值:

  • "undefined":表示值未定义;
  • "boolean":表示值为布尔值;
  • "string":表示值为字符串;
  • "number":表示值为数值;
  • "object":表示值为对象(而不是函数)或null
  • "function":表示值为函数;
  • "symbol":表示值为符号

调用 typeof null 返回的是"boject",这是因为特殊值null被认为是一个空对象的引用。

4.2 Undefined类型

该类型只有一个值,就是undefined。当使用var或let声明了变量但没有赋值时,就相当于给变量赋值了undefined值。

对于未声明的变量,只能执行一个有用的操作,就是对它调用typeof。无论是声明还是未声明,typeof 返回的都是字符串"undefined"

4.3 Null类型

Null类型只有一个值,即特殊值null。null值表示一个空对象指针

在定义将来要保存对象值的变量时,建议使用null来初始化,不要使用其他值。

console.log(null == undefined); // true
console.log(null === undefined); // false

4.4 Boolean类型

Boolean有两个字面量:true 和 false。(布尔值 true 和 false是区分大小写的,True 和 False是有效的标识符)

其他类型转Boolean可以调用Boolean(),下表总结了不同类型与布尔值之间的转换规则:

数据类型转换为true的值转换为false的值
Booleantruefalse
String非空字符串“”(空字符串)
Number非零数值(包括无穷值)0、NaN
Object任意对象null
UndefinedN/A(不存在)undefined
Boolean([]) // true
Boolean({}) // true

if等流控制语句会制动执行其他类型值到布尔值的转换

4.5 Number类型

Number类型使用IEEE 754格式表示整数和浮点数(即双精度)。不同的数值类型有对应不同的数值字面格式:

  • 十进制:直接表达即可;
  • 八进制:0 开头,如 075;当字面量中包含的数字超过了应有范围,就会忽略前面的 0,后面的数字序列会被当做十进制处理,如 08 就是十进制的 8。(八进制字面量在严格模式下是无效的,会导致js引擎抛出语法错误。ECMAScript 2015或ES6中的八进制以 0o 开头;严格模式下 0 会被视为语法错误,如果要表达八进制值,应该使用 0o
  • 十六进制:0x 开头。

使用八进制和十六进制格式创建的数值在所有数学操作中都会被视为十进制

console.log(010 + 010) // 16
console.log(0x10 + 0x10) // 32

由于js保存数值的方式,在实际中可能存在正零(+0)或负零(-0),但它们在所有情况下都被认为是等同的。

(+0) === (-0) // true

① 浮点值

  • 要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。(ECMAScript总是想方设法把值转换为整数。在小数点后面没有数字的情况下,数值就会变成整数。如果数值本身是整数,只是小数点后面跟着 0,那么它也会被当成整数。
1.0 + 1.0 // 2
  • 科学计数法:3.125e-17、2e7等(e前为系数,e后为要乘以 10 的几次方幂)。
  • 注意: 由于浮点值的精度是有限的,所以在计算中不要去判断两个浮点数的和是不是等于另一个浮点数,不要去测试某个特定的浮点数,如 0.1 + 0.2 == 0.3 // false

② 值的范围

  • 由于内存的限制,ECMAScript并不支持表示这世界上所有的数值。ECMAScript可以表示的最小值保存在 Number.MIN_VALUE 中,可以表示的最大值保存在 Number.MAX_VALUE.
  • 如果数值超出了JS可以表示的范围,那么这个数会自动转换为一个特殊的值 Infinity 。任何无法表示的负数以-Infinity(负无穷大,可用 Number.POSITIVE_INFINITY 取得),任何无法表示的正数以 Infinity (正无穷大,可用 Number.NEGATIVE_INFINITY 取得)表示。
  • 要确定一个数是不是有限大(即介于js能表示的最小值和最大值之间),可以用 isFinite() 函数(返回false为无穷值,返回true为非无穷值)。

③ NaN

有一个特殊的值叫 NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的差欧总失败了(而不是抛出错误)。

  console.log(0/0); // NaN
  console.log(-0/+0); // NaN
  console.log(5/0); // Infinity
  console.log(5/-0); // -Infinity
  • 任何涉及 NaN的操作始终会返回 NaN
  • NaN不等于 NaN在内的任何值
  • isNaN() 函数可以判断传入参数是否为数值
  console.log(isNaN(NaN)); // true
  console.log(isNaN(10)); // false,10是数值
  console.log(isNaN("11")); // false,可以转换为数值11
  console.log(isNaN("hsy")); // true,不可以转换为数值
  console.log(isNaN(true)); // false,可以转换为数值1

④ 数值转换

  • Number():规则如下
类型规则
Booleantrue转换为1,false转换为0
Number直接返回
null返回0
undefined返回NaN
String如果字符串包含数值,数值前面带加减号的情况,则会被转换为一个十进制的数值(会忽略前面的0);
如果字符串包含有效的浮点值格式,则会被转换为相应的浮点值(会忽略前面的0);
如果字符串包含有效的十六进制格式,则会被转换为该十六进制对应的十进制整数值;
如果是空字符串,则返回0;
其他情况返回NaN。
Object调用valueOf()方法,并按照上述规则转换返回值。如果转换的结果是NaN,则调用toString()方法,在按照转换字符串的规则转换

一元加操作符遵循与Number()相同的转换规律

1 + 1 // 2
1 + '1' // '11' 一元加操作符有隐形转换字符串类型的特性
'1' + '1' // '11'
  • parseInt()

专注于字符串是否包含数值的模式。字符串前面的空格会被忽略,从第一个非空字符开始转换。
如果第一个字符不是数值字符、加号、减号,立即返回NaN(空字符串也会返回NaN)。
如果第一个字符是数值字符、加号、减号,则会继续依次检测每个字符,直至字符串末尾或碰到非数值字符串。如“123hsy”会被转换为123,“22.7”会被转换为22。
parseInt()也能识别不同进制格式。但如果字符串以“0”开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。
不同数值格式很容易混淆,因此parseInt()可以传入第二个参数,用于指定底数(进制数),当提供了进制参数时,进制格式前面的“0x”可以省略。如parseInt(‘AF’,16)返回175。建议始终传入第二个进制参数

Number('010') // 10
parseInt('010') // 10
parseInt('010', 8) // 8
parseInt('010hsy', 8) // 8
parseInt('010.1') // 10
parseInt('0x010') // 16
  • parseFloat()

parseInt()类似。但其第一次解析到的小数点是有效的。它始终忽略字符串开的的零,十六进制数值始终会返回0。它只能解析十进制,但能识别所有浮点格式(包括科学计数法)。如果字符串表示整数(没有小数点或小数点后面只有零),则会返回整数(js总是想方设法地把数转化为整数)

4.6 String类型

String字符串类型表示零个或多个16位Unicode字符序列。可用单引号、双引号或反引号标示。

① 字符字面量

字面量含义
\n换行
\t制表
\b退格
\r回车
\f换页
\xnn以十六进制编码nn表示的字符(其中n为十六进制数字)
\unnnn以十六进制编码的Unicode字符(其中n为十六进制数字)

字符串的长度可以通过length属性获取,这个属性返回字符串中16位字符的个数。如果字符串中包含双字节字符,那么length属性返回的值可能不是准确的字符数。

② 字符串的特点

ECMAScript中的字符串是不可变的,一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。

③ 转换为字符串

  • toString():可用于数值、布尔值、对象和字符串值。null 和 undefined 值没有该方法。在对数值调用时,可以接收一个底数传参,即以什么底数来输出数值的字符串表示。
  • String():如果不确定一个值是不是null 或 undefined ,可以使用该方法。如果值有toString()方法,则调用该方法(不传参)并返回结果;如果值是null,返回“null”;如果值是undefined,返回“undefined”。
  • 用加号操作符给一个值加上一个空字符串"",也可以将其转换为字符串。

④ 模板字面量

用反引号`包裹,与使用单引号或双引号不用,模板字面量保留换行字符,可以跨行定义字符串。模板字面量会保持反引号内部的空格。

⑤ 字符串插值

字符串插值通过${}在模板字面量里使用一个js表达式。所有插入的值都会使用toString()强制转换为字符串,任何js表达式都可以用于插入值。

⑥模板字面量标签函数

模板字面量支持定义标签函数,而通过便签函数可以自定义插值行为。

  let a = 6;
  let b = 9;

  function simpleTag(strings, ...expressions) {
    console.log(strings);
    for (const expression of expressions) {
      console.log(expression);
    }

    return 'foobar';
  }
  let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
  // ["", " + ", " = ", ""]
  // 6
  // 9
  // 15

  console.log(taggedResult); // foobar

  function zipTag(strings, ...expressions) {
    return strings[0] + 
      expressions.map((e, i) => `${e}${strings[i + 1]}`)
        .join('');
  }

  let taggedResult2 = zipTag`${ a } + ${ b } = ${ a + b }`;
  console.log(taggedResult2); // 6 + 9 = 15

⑦ 原始字符串

使用模板字面量可以直接获取原始的模板字面量内容,而不是被转换后的字符表示。为此可以使用磨人的String.raw 标签函数。

4.7 Symbol类型

符号是原始值,且符号实例是唯一、不可变的 。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而作用非字符串形式的对象属性。

① 符号的基本用法

符号需要使用 Symbol() 函数初始化。

let sym = Symbol();
console.log(typeof sym); // symbol

调用 Symbol() 函数时,可以传入一个字符串作为对符号的描述,将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全没有关系。

let sym1 = Symbol('foo');
let sym2 = Symbol('foo');
console.log(sym1 == sym2); // false

符号没有字面量语法。

Symbol() 函数不能与new关键字一起作为构造函数使用。

let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"

let myString = new String();
console.log(typeof myString); // "object"

let myNumber = new Number();
console.log(typeof myNumber); // "object"

let mySymbol = new Symbol(); //  TypeError: Symbol is not a constructor

② 使用全局符号注册表

如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,使用 Symbol.for() 在全局符号注册表中创建并重用符号。

let fooSymbol = Symbol.for('foo');
console.log(typeof fooSymbol); // symbol

Symbol.for() 对没分字符串键都执行幂等操作。第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应符号,就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用会同样检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

let fooSymbol = Symbol.for('foo'); // 创建新符号
let otherFooSymbol = Symbol.for('foo'); // 重用已有符号
console.log(fooSymbol === otherFooSymbol); // true

即使使用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol() 定义的符号也并不相等。

let fooSymbol = Symbol.for('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol === otherFooSymbol); // false

全局注册表中的符号必须使用字符串来创建,因此作为参数传给Symbol.for() 的任何值都会被转换为字符串。注册表中使用的键同时也会被用作符号描述。

let mySymbol = Symbol.for();
console.log(mySymbol); // Symbol(undefined)

可以使用 Symbol.keyFor() 来查询全局注册表,这个方法接收符号,返回该全局符号对应的字符串键。如果查询的不是全局符号,则返回undefined。如果传给 Symbol.keyFor() 的不是符号,则该方法抛出TypeError。

let mySymbol1 = Symbol.for('foo');
console.log(Symbol.keyFor(mySymbol1)); // foo

let mySymbol2 = Symbol('foo');
console.log(Symbol.keyFor(mySymbol2)); // undefined

Symbol.keyFor(123); // TypeError: 123 is not a symbol

③ 使用符号作为属性

凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这包括了对象字面量属性和Object.defineProperty() / Object.defineProperties() 定义的属性。对象字面量只能在计算属性语法中使用符号作为属性。

let s1 = Symbol('foo'),
    s2 = Symbol('bar'),
    s3 = Symbol('baz'),
    s4 = Symbol('qux');
    
let o = {
    [s1]: 'foo val'
};
console.log(o); // { [Symbol(foo)]: 'foo val' }

Object.defineProperty(o, s2, {value: 'bar val'});
console.log(o); // { [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val' }

Object.defineProperties(o, {
    [s3]: {value: 'baz val'},
    [s4]: {value: 'qux val'}
})
console.log(o); // { [Symbol(foo)]: 'foo val', [Symbol(bar)]: 'bar val', [Symbol(baz)]: 'baz val', [Symbol(qux)]: 'qux val' }

类似于 Object.getOwnPropertyNames() 返回对象实例的常规属性数组, Object.getOwnPropertySymbols() 返回实例的符号属性数组。这两个方法的返回值彼此互斥。 Object.getOwnPropertyDescriptors() 会返回同时包含常规和符号属性描述符的对象。 Reflect.ownKeys() 会返回两种类型的键。

let s1 = Symbol('foo'),
    s2 = Symbol('bar');
    
let o = {
    [s1]: 'foo val',
    [s2]: 'bar val',
    baz: 'baz val',
    qux: 'qux val'
};

console.log(Object.getOwnPropertyNames(o)); // [ 'baz', 'qux' ]
console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(foo), Symbol(bar) ]
console.log(Object.getOwnPropertyDescriptors(o)); // { baz: {...}, qux: {...}, [Symbol(foo)]: {...}, [Symbol(bar)]: {...}}
console.log(Reflect.ownKeys(o)); // [ 'baz', 'qux', Symbol(foo), Symbol(bar) ]

④ 常用内置符号

在提到ECMAScript规范时,经常会引用符号在规范中的名称,前缀为 @@。 比如@@iterator指的是Symbol.iterator。

⑤ Symbol.asyncIterator (待更)

⑥ Symbol.hasInstance (待更)

⑦ Symbol.isConcatSqreadable (待更)

⑧ Symbol.iterator(待更)

⑨ Symbol.match (待更)

⑩ Symbol.replace (待更)

⑪ Symbol.search (待更)

⑫ Symbol.species (待更)

⑬ Symbol.split(待更)

⑭ Symbol.toPrimitive(待更)

⑮ Symbol.toStringTag(待更)

4.8 Object类型

对象其实就是一组数据和功能的集合。每个Object对象实例都有以下属性和方法:

  • constructor :用于创建当前对象的函数。
  • hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给行的属性。要检查的属性名必须是字符串。
  • isPrototypeOf(object):用于判断当前对象是否为另一个对象的原型。
  • propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。要检查的属性名必须是字符串。
  • toLocaleString():返回对象的字符串表示,改字符串反映对象所在的本地执行环境。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象对应的字符串、数值、或布尔值表示。通常与toString()的返回值相同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值