JavaScript基础汇总(ES6)

JavaScript语言基础(ES6)

概述

ECMA-262第5版(ES5)定义的ECMAScript,是目前为止实现得最广泛的一个版本。第6版在浏览器中的实现程度次之。不过到2017年底,大多数浏览器几乎都实现了这一版的规范。本篇介绍的内容基于ES6,ES6在语言基础方面和以前的差别不是很大,本文中涉及ES6新增部分,会通过说明标出。

0 引入JS

① 行内式JS

<input type="button" value="点我试试" onclink="javascript:alert('Hello World')" />
  1. 可以将单行或少量JS代码写在HTML标签的事件属性中(以on开头的属性),如: onclink
  2. 注意单双引号的使用:在HTML中我们推荐使用双引号,JS中我们推荐使用单引号
  3. 可读性差,在 HTML 中编入 JS 大量代码时,不方便阅读
  4. 特殊情况下使用

② 内嵌式JS

<script>
     alert('Hello World!');
</script>
  • 可以将多行JS代码写到<script>标签中
  • 内嵌 JS 是学习时常用的方式
  • 可以放在head标签之间,等待js执行完后再渲染页面
  • 更多的是将其放在前面,不阻塞页面的渲染

③ 外部JS

<script src="my.js"> </script>
  1. 利于HTML页面代码结构化,把单独JS代码独立到HTML页面之外,既美观,又方便
  2. 引用外部JS文件的script标签中间不可以写代码
  3. 适合于JS代码量比较大的情况

1 语法

1.1 区分大小写

​ 要理解的第一个概念就是 ECMAScript 中的一切(变量、函数名和操作符)都区分大小写。这也就 意味着,变量名 test 和变量名 Test 分别表示两个不同的变量,而函数名不能使用 typeof,因为它是一个关键字,但 typeOf 则完全可以是一个有效的函数名。

1.2 标识符

​ 所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则 组合起来的一或多个字符:

  • 第一个字符必须是一个字母、下划线(_)或一个美元符号($)
  • 其他字符可以是字母、下划线、美元符号或数字。 标识符中的字母也可以包含扩展的 ASCII 或 Unicode 字母字符(如 À 和 Æ),但我们不推荐这样做。
  • 按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的 首字母大写,例如: 任 firstSecond myCar doSomethingImportant

1.3 注释

ECMAScript 使用 C 风格的注释,包括单行注释和块级注释,如下所示:

// 单行注释

块级注释以一个斜杠和一个星号(/)开头,以一个星号和一个斜杠(/)结尾,如下所示:

/*
	多行注释
*/    

1.4 严格模式

ECMAScript 5 引入了严格模式(strict mode)的概念。严格模式是为 JavaScript 定义了一种不同的 解析与执行模型。在严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。

① 严格模式的引入

要在整个脚本中启用严格模式,可以在顶部添加如下代码:

"use strict"; 

这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma),用于告诉支持的 JavaScript 引擎切换到严格模式。这是为不破坏 ECMAScript 3 语法而特意选定的语法。
函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:

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

严格模式下,JavaScript 的执行结果会有很大不同,支持严格模式的浏览器包括 IE10+、Firefox 4+、Safari 5.1+、Opera 12+和 Chrome。

② 严格模式下的区别

(1) 变量规定

  • 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量
  • 严格模式禁止这种用法,变量都必须先用var 命令声明,然后再使用
  • 严禁删除已经声明变量,例如,``delete x` 语法是错误的
<body>
    <script>
        'use strict';
        // 1. 我们的变量名必须先声明再使用
        // num = 10;
        // console.log(num);
        var num = 10;
        console.log(num);
        // 2.我们不能随意删除已经声明好的变量
        // delete num;
    </script>
</body>

(2) 严格模式下this指向问题

  • 普通模式下在全局作用域函数中的this指向window对象
  • 严格模式下全局作用域中函数中的this 是 undefined
  • 普通模式下构造函数,不加 new 也可以调用,当普通函数,this指向全局对象
  • 严格模式下,如果构造函数不加 new 调用,this指向的是 undefined ,如果给它赋值,会报错
  • new 实例化的构造函数指向创建的对象实例
  • 定时器this 还是指向window
  • 事件、对象还是指向调用者
<body>
    <script>
        'use strict';
		//3. 严格模式下全局作用域中函数中的 this 是 undefined。
        function fn() {
            console.log(this); // undefined。

        }
        fn();
        //4. 严格模式下,如果构造函数不加new调用, this 指向的是undefined 如果给他赋值则会报错.
        function Star() {
            this.sex = '男';
        }
        // Star();
        var ldh = new Star();
        console.log(ldh.sex);
        //5. 定时器 this 还是指向 window 
        setTimeout(function() {
            console.log(this);

        }, 2000);
        
    </script>
</body>

(3) 函数变化

  • 函数不能有重名的参数
  • 函数必须声明在顶层,新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数
<body>
    <script>
        'use strict';
        // 6. 严格模式下函数里面的参数不允许有重名
        function fn(a, a) {
           console.log(a + a);

        };
        // fn(1, 2);
        function fn() {}
    </script>
</body>

2 关键字与保留字

2.1 关键字

ECMA-262 描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或 者用于执行特定操作等。按照规则,关键字也是语言保留的,不能用作标识符。


break       do          in            typeof
case        else        instanceof    var
catch       export      new           void
class       extends     return        while
const       finally     super         with
continue    for         switch        yield
debugger    function    this
default     if          throw
delete      import      try

2.2 保留字

ECMA-262第6版为将来保留的所有词汇

始终保留:

enum

严格模式下保留:

implements  package     public
interface   protected   static
let         private

模块代码中保留:

await

3 变量

3.1 var关键字

1 var声明的范围是函数作用域

function test() {
       var message = "hi";
}
test();
//console.log(message); 出错 Uncaught ReferenceError: message is not defined

2 var声明提升

function foo() {
  console.log(age);
  var age = 18;
}
foo(); //输出undefined
        
// 相当于如下代码
// 所谓“提升”,也就是把所有变量声明都拉到函数作用域的顶部。
function foo() {
  var age;
  console.log(age);
  age = 18;
}

3 可以用var反复声明同一个变量

var声明提升,也就是把所有变量都拉到函数作用域的顶部。因此,反复多次使用var声明同一个变量也没有问题。

JavaScript引擎会自动将多余的声明在作用域顶部合并为一个声明。

function foo() {
    var age = 16;
    var age = 26;
    var age = 36;
    console.log(age);
}
foo(); //输出36

接下来介绍的let和const都是es6新增的。

3.2 let声明

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

if(true) {
 var name = "Matt";
 console.log(name); //输出Matt
}
console.log(name); //输出Matt

if(true) {
 let age = 18;
 console.log(age); //输出18
}
console.log(age); // Uncaught ReferenceError: age is not defined

2 JavaScript引擎会记录用于变量声明的标识符极其所在的块作用域

因此,嵌套使用相同的标识符不会报错,而这是因为同一块中没有重复声明

var name = 'Jack';
console.log(name); //输出Jack
if(true) {
 var name = 'Matt';
 console.log(name); //输出Matt
}
console.log(name); //输出Matt

let age = 30;
console.log(age); //输出30
if(true) {
  let age = 26;
  console.log(age); //输出26
}
console.log(age); //输出30

3 let不允许同一个块作用域中出现冗余声明

对声明冗余报错不会因混用let和var而受影响。这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在。

let age;
let age; //SyntaxError:标识符age已经声明过了

var age;
let age; //SyntaxError:标识符age已经声明过了

3.3 const声明

1 const的行为与let基本相同

唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时出错。

const age = 26;
age = 36; //TypeError:给常量赋值

2 const声明的作用域也是块

const name = 'Matt';
if(true) {
    const name = 'Nicholas';
}
console.log(name); //输出Matt

3 const也不允许重复声明

const name = 'Matt';
const name = "Jack"; //SyntaxError

4 const声明的限制只适用于它指向的变量引用

换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的熟悉并不违反const的限制。

const person = {};
person.name = 'Matt'; // ok

3.4 声明风格

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

4 数据类型

​ ES6 中有 6 种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number 、String和Symbol。还有 1种复杂数据类型——Object,Object 本质上是由一组无序的名值对组成的。

Symbol是ES6新增的简单数据类型。

4.1 typeof操作符

是一个操作符,不是函数,不需要参数(也可以使用参数)

let aa="cjd"
typeof aa	//string
typeof(aa)	//string
typeof 11	//number

使用typeof可以返回如下值

  • undefined——值未定义
  • boolean——值为布尔值
  • string——值为字符串
  • number——值为数值
  • object——值为对象或null
  • function——值为函数
  • symbol——值为符号

4.2 Undefined类型

1 Undefined类型只有一个值,就是特殊值undefined。

当使用var或let声明了变量但是没有初始化时,就相当于给变量赋值undefined

let message;
console.log(message == undefined); //true

2.1 注意:包含undefined值的变量和未定义变量是有区别的

let message;//这个变量被声明了,只是值为undefined
console.log(message);//输出“undefined”
console.log(age);//报错

2.2 注意:对未声明的变量,只能执行一个有用的操作,就是对它调用typeof

let message;
console.log(typeof message);//输出“undefined”
console.log(typeof age);//输出“undefined”

无论是声明后未初始化还是未声明,typeof返回的都是字符串undefined。

逻辑上讲这是对的,因为虽然二者存在根本性差异,但是它们都无法执行实际操作

建议在声明变量的时候同时进行初始化,这样若typeof返回undefined时,就可以知道那时因为给定的变量未声明,而不是声明了但未初始化。

3 undefined是一个假值

但是假值有多个,如false,null,undefined,“”,0,要明确自己检验的是undefined这个字面值还是假值

let message;
if(message){
    //这块不会被执行,因为message的初值是undefined,是个假值
}
if(!message){
    //这块会被执行
}
if(age){
    //由于age未声明,这里会报错

4.3 Null类型

1 Null类型同样只有一个值,即特殊值null。

逻辑上讲,null值表示一个空对象指针

let car = null;
console.log(typeof car); //输出object

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

这样,只要检查这个变量的值是不是null就可以知道这个变量是否在后来被重新赋予了一个对象的引用

if(car!=null){
    //car是一个对象的引用
}

3 undefined的值是由null值派生而来,因此ECMA-262将它们定义为表面上相等,

但是即使null和undefined有关系,它们的用途也是完全不一样的。

一般不必显示地将变量值设置为undefined。

但是null不是这样的,任何时候,只要变量保存对象,而当时又没有那个对象可保存,就要使用null来填充该变量,这样就可以保持null是空对象指针的语义,并进一步将其与undefined区分开来。

console.log(null == undefined); //输出true,表明相等

4.4 Boolean类型

  • Bollean有两个字面值true和false,而且布尔值字面量true和false区分大小写
  • 布尔型和数字型相加的时候, true 的值为 1 ,false 的值为 0。

4.5 Number类型

1 整数值表示

整数值的表示可以有二进制、十进制、八进制和十六进制。下面重点介绍八进制和十六进制表示。

八进制

let octalNum1 = 0o70;//八进制的56
let octalNum2 = 0o17;//八进制的15
console.log(octalNum1);
console.log(octalNum2);

十六进制

let hexNum1 = 0xA;//十六进制10
let hexNum2 = 0x1f;//十六进制31
console.log(hexNum1);
console.log(hexNum2);

2 浮点值

2.1要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字

let floatNum1 = 1.1;

2.2因为存储浮点值使用的内存空间是存储整数值的两倍,ECMAScript总是想方设法把数值转换为整数

在小数点后面没有数字的情况或数值本身就是整数,则会被转换为整数

let floatNum2 = 1.;//小数点后面没有数字,当成整数1处理

let floatNum3 = 10.0;//小数点后面是零,当成整数10处理

2.3科学计数法

对于特别小或特别大的数值,浮点值可以用科学计数法表示

格式:一个数值(整数或浮点数)后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂

let floatNum = 3.125e7//等于31250000

2.4浮点值的精确度最高可达17位小数,但是在算数计算中远不如整数精确。

例如0.1加0.2得到的不是0.3,而是0.30000000000000004,存在着微小的舍入错误,要避免某个特定的浮点值

if(a+b == 0.3) {//别这么干
    console.log("You got 0.3.");
}

3 值的范围

3.1 如果计算得到的数值结果超出了JavaScript可以表示的范围,那么这个数值会被自动转换为一个特殊的Infinity值

任何无法表示的负数以-Infinity表示,任何无法表示的正数用Infinity表示

如果计算返回正infinity或负infinity,则改值不能再进一步用于任何计算

3.2 可以使用isFinite()函数确定一个值是不是有限大

let result = Number.MAX_VALUE + Number.MAX_VALUE;

console.log(isFinite(result));//false

4 NaN
4.1 特殊值NaN,意思是“不是数值”,用于表示本来要返回数值的操作失败了

console.log(0/0);//NaN
console.log(-0/+0);//NaN
console.log(5/0);//Infinity
console.log(5/-0);//-Infinity

4.2 isNaN()函数接收任意数据类型,然后判断这个参数是否“不是数值”

isNaN

该函数会尝试把参数转换为数值

console.log(isNaN(NaN));//true
console.log(isNaN(10));//false
console.log(isNaN("10"));//false
console.log(isNaN("blue"));//true
console.log(isNaN(true));//false

4.6 String类型

1 字符串引号
可以使用双引号、单引号或反引号

let firstName = "John";
let secondName = 'Jacob';
let lastName = `Jack`;

注意:开头和结尾的引号要一致,反引号中的换行符不会被忽略,而其他引号中若有换行符,会报语法错

let firstName = 'Bill"; //语法错误
let secondName = `123
123`;

2 字符串转义符

以下列举了部分常用的转义符

转义符解释说明
\n换行符,n是newline
\ \斜杠\
\ ’’ 单引号
\ ‘’‘’ 双引号
\ ttab 缩进
\ b空格,b是blank的意思

3 字符串长度

转义序列表示一个字符,比如下面虽然包括6个字符长的转义序列,变量text仍然是28个字符

let text = "This is the letter sigma: \u03a3.";
console.log(text.length);//28

4 模板字面量

==ES6新增了使用模板字面量定义字符串的能力。==与使用单引号或双引号不同,模板字面量可以保留换行字符。

let myMultiLineString = `first line\nsecond line`;
let myMultiLineTemplateLiteral = `first line
second line`;
console.log(myMultiLineString);
console.log(myMultiLineTemplateLiteral);

顾名思义,模板字面量在定义模板时特别有用,比如下面这个HTML模板:

let pageHTML = `
<div>
	<a href="#">
		<span>Jake</span>
	</a>
<div>`;

由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。格式正确的模板字符串看起来可能会缩进不当:

let myTemplateLiteral = `first line
       second line`;
console.log(myTemplateLiteral.length);//29,second前面有7个空格


let myTemplateLiteral = `first line
second line`;
console.log(myTemplateLiteral.length);//22

5 字符串插值

字符串插值通过${}中使用一个JavaScript表达式实现:

let value = 5;
let exponent = 'second';
//以前,字符串插值是这样实现的
let interpolatedString = value + ' to the ' + exponent + ' power is ' + (value * value);
//现在,可以使用模板字面量这样实现:
let interpolatedTemplateLiteral = `${value} to the ${exponent} power is ${value * value}`;

console.log(interpolatedString)
console.log(interpolatedTemplateLiteral)

4.7 Symbol类型

1 关于Symbol
1.1 什么是Symbol
Symbol(符号)是ES6新增的数据类型。Symbol是原始值,且符号实例是唯一、不可变的。
Symbol(符号)的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
1.2 理解Symbol
看了上面关于Symbol的定义,可能还不能够很清楚Symbol是什么,为什么ES6要新增Symbol类型
对此,需要抓住定义中的两个核心去理解Symbol,一个是原始值、一个是唯一
由于篇幅有限,要展开去解释Symbol的背景和用途的话,会有点冗余,所以下面推荐几篇博客用于理解Symbol
关于原始值和引用值的区别
https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive
https://blog.csdn.net/qq_22896159/article/details/81000178
Symbol的另一个核心是唯一,而这也决定了其用途,可以看一下下面两篇博客了解Symbol诞生的背景和用途
https://blog.csdn.net/weixin_34124939/article/details/91456169
https://blog.csdn.net/weixin_30699741/article/details/98461284?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7EHighlightScore-1.queryctrv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ELandingCtr%7EHighlightScore-1.queryctrv2&utm_relevant_index=2

2 Symbol的用法

2.1 符号的基本用法

直接使用Symbol()创建新的symbol类型,并用一个可选的字符串作为其描述。

var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');

上面的代码创建了三个新的symbol类型。
注意,Symbol(“foo”) 不会强制将字符串 “foo” 转换成symbol类型。它每次都会创建一个新的 symbol类型
Symbol函数的字符串参数,用作对符号的描述,将来可以通过该字符串来调试代码
但是,这个字符串参数与符号定义或标识完全无关

Symbol("foo") === Symbol("foo"); // 输出false

带有 new 运算符的语法将抛出 TypeError 错误:

var sym = new Symbol(); // TypeError

这会阻止创建一个显式的 Symbol 包装对象而不是一个 Symbol 值。
围绕原始数据类型创建一个显式包装对象从 ES6 开始不再被支持。
然而,现有的原始包装对象,如 new Boolean、new String以及new Number,因为遗留原因仍可被创建。
如果你真的想创建一个 Symbol 包装器对象 (Symbol wrapper object),你可以使用 Object() 函数:

var sym = Symbol("foo");
typeof sym;     // "symbol"
var symObj = Object(sym);
typeof symObj;  // "object"

2.2 使用全局符号注册表

上面使用Symbol() 函数的语法,不会在你的整个代码库中创建一个可用的全局的symbol类型。

要创建跨文件可用的symbol,甚至跨域(每个都有它自己的全局作用域),

使用 Symbol.for() 方法Symbol.keyFor() 方法从全局的symbol注册表设置和取得symbol

(1) Symbol.for()
可以用Symbol.for()在全局符号注册表中创建并重用符号

let fooGlobalSymbol = Symbol.for('foo');
console.log(typeof fooGlobalSymbol);

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

let fooGlobalSymbol = Symbol.for('foo');
let otherFooGlobalSymbol = Symbol.for('foo');
console.log(fooGlobalSymbol == otherFooGlobalSymbol);//true

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

let localSymbol = Symbol('foo');
let globalSymbol = Symbol.for('foo');
console.log(localSymbol == globalSymbol); //false

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

此外,注册表中使用的键同时也会被用作符号描述

let emptyGlobalSymbol = Symbol.for();

console.log(emptyGlobalSymbol); //Symbol(undefined)

(2) Symbol.keyFor()

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

let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); //foo
let s2 = Symbol('bar')
console.log(Symbol.keyFor(s2)); //undefined

3 使用符号作为属性

凡是可以使用字符串或数值作为属性的地方,都可以使用符号。这就包括了对象字面量属性和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):
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.getOwnPropertySymbols(o));
// [Symbol(foo), Symbol(bar)]
console.log(Object.getOwnPropertyNames(o));
// ["baz","qux"]
console.log(Object.getOwnPropertyDescriptors(o));
// {baz: {...}, qux:{...}, Symbol(foo):{...}, Symbol(bar): 
console.log(Reflect.ownKeys(o));
// ["baz","qux",Symbol(foo), Symbol(bar)]

4.8 Object类型

ECMAScript 中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建 的对象类型的名称来创建。而创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定义对象,如下所示:

 var o = new Object();  

仅仅创建 Object 的实例并没有什么用处,但关键是要理解一个重要的思想:即在 ECMAScript 中, (就像 Java 中的 java.lang.Object 对象一样)Object 类型是所有它的实例的基础。换句话说, Object 类型所具有的任何属性和方法也同样存在于更具体的对象中。 Object 的每个实例都具有下列属性和方法。

  • constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor) 就是 Object()。
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例 的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例 如:o.hasOwnProperty(“name”))。
  • isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型。
  • propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符 串形式指定。
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象对应的字符串、数值或布尔值表示。通常与 toString()方法的返回值 相同

由于在 ECMAScript 中 Object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法。在此,只是对Object做了简单的介绍,后续会对Object做更详细的介绍。

5 数据类型转换

①转换为字符串型

方式说明案例
toString()转成字符串var num = 1; alert(num.toString());
String()强制转换转成字符串var num = 1; alert(String(num));
加号拼接字符串和字符串拼接的结果都是字符串var num =1; alert(num+“我是字符串”);
//1.把数字型转换为字符串型 toString()  变量.toString()
var num = 10;
var str = num.toString();
console.log(str);

//2.强制转换
console.log(String(num));
  • null 和 undefined 值没有toString()这个方法。
  • 在不知道要转换的值是不是 null 或 undefined 的情况下,还可以使用转型函数 String(),这个函数能够将任何类型的值转换为字符串。
  • 三种转换方式,我们更喜欢用第三种加号拼接字符串转换方式,这一方式也称为隐式转换

②转换为数字型

方式说明案例
parselnt(string)函数将string类型转成整数数值型parselnt(‘78’)
parseFloat(string)函数将string类型转成浮点数数值型parseFloat(‘78.21’)
Number()强制转换函数将string类型转换为数值型Number(‘12’)
js 隐式转换(- * /)利用算术运算隐式转换为数值型‘12’-0
// 1.parseInt()
var age =prompt('请输入您的年龄');
consolo.log(parseInt(age));  //数字型18
consolo.log(parseInt('3.14'));  //3取整
consolo.log(parseInt('3.94'));  //3,不会四舍五入
consolo.log(parseInt('120px'));  //120,会去掉单位

// 2.parseFloat()
console.log(parseFloat('3.14'));  //3.14
consolo.log(parseFloat('120px'));  //120,会去掉单位


// 3.利用Number(变量)
var str ='123';
console.log(Number(str));
console.log(Number('12'));   

// 4.利用了算术运算 - * /   隐式转换
console.log('12'-0);  // 12
console.log('123' - '120');  //3
console.log('123' * 1);  // 123

1.注意 parseInt 和 parseFloat ,这两个是重点

2.隐式转换是我们在进行算数运算的时候,JS自动转换了数据类型

③转换为布尔型

方法说明案例
Boolean()函数其他类型转成布尔值Boolean(‘true’);
console.log(Boolean('')); //false
console.log(Boolean(0));  //false
console.log(Boolean(NaN)); //false
console.log(Boolean(null)); //false
console.log(Boolean(undefined)); //false
console.log(Boolean('小白')); //true
console.log(Boolean(12));   //true

下表总结了不同类型和布尔值之间的转换规则

数据类型转换为true的值转换为false的值
Booleantruefalse
String非空字符串“”
Number非零数值0,NaN
Object任意对象null
UndefinedN/A(不存在)undefined

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

6 操作符

6.1 算数运算符

运算符描述实例
+10 + 20 =30
-10 - 20 =-10
*10 * 20 =200
/10 / 20 =0.5
%取余数(取模)返回出发的余数 9 % 2 =1

6.2 递增和递减运算符

递增(++)

递减(- -)

放在变量前面时,我们称为前置递增(递减)运算符

放在变量后面时,我们称为后置递增(递减)运算符

注意:递增和递减运算符必须和变量配合使用。

①前置递增运算符

使用口诀:先自加,后返回值

var num = 10;
alert (++num + 10); // 21
②后置递增运算符

使用口诀:先返回原值,后自加

var num = 10;
alert(10 + num++); // 20

6.3 比较(关系)运算符

比较运算符是两个数据进行比较时所使用的运算符,比较运算后,会返回一个布尔值(true / false)作为比较运算的结果。

运算符名称说明案例结果
<小于号1 < 2true
>大于号1 > 2false
>=大于等于号(大于或者等于)2 >= 2true
<=小于等于号(小于或者等于)3 <= 2false
==判等号(会转型)37 == 37true
!=不等号37 != 37false
===全等 要求值和数据类型都一致37 === ‘37’false

不同等号之间的区别

符号作用用法
=赋值把右边给左边
==判断判断两边值是否相等(注意此时有隐士转换)
===全等判断两边的值和数据类型是否完全相同
console.log(18 == '18');		//true
console.log(18 === '18');		//false

6.4 逻辑运算符

逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值

逻辑运算符说明案例
&&“逻辑与”,简称"与" andtrue && false
||“逻辑或”,简称"或" ortrue || false
“逻辑非”,简称"非" not!true

逻辑与:两边都是 true才返回 true,否则返回 false

逻辑与

逻辑或:两边都为 false 才返回 false,否则都为true

逻辑或

逻辑非:逻辑非(!)也叫作取反符,用来取一个布尔值相反的值,如 true 的相反值是 false

var isOk = !true;
console.log(isOk);  // false
//逻辑非(!)也叫作取反符,用来取一个布尔值相反的值,如 true 的相反值是 false

6.5 赋值运算符

概念:用来把数据赋值给变量的运算符。

赋值运算符说明案例
=直接赋值var usrName = ‘我是值’
+= ,-=加,减一个数后再赋值var age = 10; age+=5;//15
*=,/=,%=成,除,取模后再赋值var age = 2; age*=5; //10
var age = 10;
age += 5;  // 相当于 age = age + 5;
age -= 5;  // 相当于 age = age - 5;
age *= 10; // 相当于 age = age * 10;

6.6 运算符优先级

优先级运算符顺序
1小括号()
2一元运算符++ – !
3算数运算符先 * / 后 + -
4关系运算符>, >= , < , <=,
5相等运算符,!=,=,!==
6逻辑运算符先 && 后 ||(先与后或)
7赋值运算符=
8逗号运算符

1.一元运算符里面的逻辑非优先级很高

2.逻辑与逻辑或 优先级高

3.练习题

console.log( 4 >= 6 || '人' != '阿凡达' && !(12 * 2 == 144) && true)	// true
var a = 3 > 5 && 2 < 7 && 3 == 4; 
console.log(a); 	//false 

var b = 3 <= 4 || 3 > 1 || 3 != 2; 
console.log(b); 	//true

var c = 2 === "2"; 
console.log(c);  	//false

var d = !c || b && a ;
console.log(d);		//true

7 控制语句

​ 流程控制语句主要有三种结构,分别是顺序结构、分支结构和循环结构,这三种结构代表三种代码执行的顺序。

下面重点介绍分支结构和循环结构。

7.1分支结构

7.1.1 if语句

大多数编程语言中最为常用的一个语句就是 if 语句。

① if 语句的语法

if (condition1) statement1 
else if (condition2) statement2 //可以有多个else if,也可以没有
else statement3  

其中的 condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。 ECMAScript 会自动调用 Boolean()转换函数将这个表达式的结果转换为一个布尔值。

  • 如果对 condition1 求值的结果是 true,则执行 statement1(语句 1)。
  • 如果对 condition1求值的结果是 false,且condition2求值的结果是true,则执行 statement2 (语句 2)。
  • 如果condition1和condition2都是false,则执行else的 statement3(语句 3)。

② 要执行的语句如果是多行,则必须用花括号括起来形成代码块,如果是单行,也推荐用花括号括起来。 请看下面的例子:

if (i < 25) {
 alert("Less than 25.");
}// 即使只有一行语句,也推荐用花括号括起来,形成代码块
else if (i < 35) {
  alert("Less than 35.");
}
else { 
 alert("Greater than or equal to 25."); 
}
7.1.2 三元表达式

语法结构 : 表达式1 ? 表达式2 : 表达式3

执行思路

如果表达式1为true,则返回表达式2的值,如果表达式1为false,则返回表达式3的值

案例:数字补0

用户输入数字,如果数字小于10,则在前面补0,比如01,09,

如果数字大于10,则不需要补,比如20

var figuer = prompt('请输入0~59之间的一个数字');
        var result = figuer < 10 ? '0' + figuer : figuer
        alert(result);
7.1.3 switch

① switch的语法

ECMAScript 中 switch 语句的语法与其他基于 C 的语言非常接近,如下所示:

switch (expression) { 
 case value: statement 
 break; 
 case value: statement 
 break; 
 case value: statement 
 break; 
 case value: statement 
 break; 
 default: statement 
}

② switch 语句中的每一种情形(case)的含义

  • “如果表达式等于这个值(value),则执行后面的 语句(statement)”。而 break 关键字会导致代码执行流跳出 switch 语句。
  • 如果省略 break 关键字, 就会导致执行完当前 case 后,继续执行下一个 case。最后的 default 关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码(因此,也相当于一个 else 语句)。

从根本上讲,switch 语句就是为了让开发人员免于编写像下面这样的代码:

if (i == 25){ 
 alert("25"); 
} else if (i == 35) { 
 alert("35"); 
} else if (i == 45) { 
 alert("45"); 
} else { 
 alert("Other"); 
} 

而与此等价的 switch 语句如下所示:

switch (i) { 
 case 25: 
 alert("25"); 
 break; 
 case 35: 
 alert("35"); 
 break; 
 case 45: 
 alert("45"); 
 break; 
 default: 
 alert("Other"); 
} 


//多个case可以合并,也就是说某个case可以不带break语句,顺序执行下去,直到遇见break才退出
switch (i) { 
 case 25: 
 /* 合并两种情形 */ 
 case 35: 
 alert("25 or 35"); 
 break; 
 case 45: 
 alert("45"); 
 break; 
 default: 
 alert("Other"); 
} 

③ switch 语句中使用任何数据类型

无论是字符串,还是对象都没有 问题。其次,每个 case 的值不一定是常量,可以是变量,甚至是表达式。请看下面这个例子:

switch ("hello world") { 
 case "hello" + " world": 
 alert("Greeting was found."); 
 break; 
 case "goodbye": 
 alert("Closing was found."); 
 break; 
 default: 
 alert("Unexpected message was found."); 
} 
//结果就会显示
//"Greeting was found."

7.2 循环结构

7.2.1 for循环

for 语句是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。以下是 for 语句的语法:

for (initialization; expression; post-loop-expression) statement 

下面是一个示例:

var count = 10; 
for (var i = 0; i < count; i++){ 
 alert(i); 
} 

以上代码定义了变量i的初始值为0。只有当条件表达式(i < count) 返回true的情况下会进入for循环,因此也有可能不会执行循环体中的代码。如果执行了循环体中的代码,则一定会对循环后的表达式(i++)求值,即递增i的值。这个for循环语句与下面的while语句功能相同。

var count = 0;

var i = 0;

while( i < count ) {
	alert(i);
	i++;
}
7.2.2 for-in语句

for-in语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:

for(property in expression) statement

下面是一个例子:

for(const propName in window) {
	document.write(propName);
}

这个例子使用for-in循环显示了BOM对象window的所有属性。

7.2.3 for-of语句

for-of语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:

for(property of expression) statement

下面是示例:

for(const el of [2,4,6,8]) {
	document.write(el);
}

这个例子中,我们使用for-of语句显示了一个包含4个元素的数组中的所有元素。

  • for-of循环会按照可迭代对象的next()方法产生值的顺序迭代元素
  • 如果尝试迭代的变量不支持迭代,则for-of语句会抛出错误
7.2.4 label语句

标签语句用于给语句加标签,语法如下:

label:statement

下面是一个例子:

start: for (let i = 0; i < count; i++) {
    console.log(i);
}

在这个例子中,start是一个标签,可以在后面通过break或continue语句引用。标签语句的典型应用场景是嵌套循环。后面讲到break和continue关键字会举例子说明。

7.2.5 while循环

while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。 因此,循环体内的代码有可能永远不会被执行。以下是 while 语句的语法:

while(expression) statement 

下面是一个示例:

var i = 0; 
while (i < 10) { 
    alert(i)
	i += 2; 
}
7.2.6 do while循环

do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。 换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的语法:

do { 
 statement 
} while (expression); 

下面是一个示例:

var i = 0; 
do { 
 i += 2; 
} while (i < 10); 
alert(i); 
7.2.7 break和continue关键字

break 和 continue 语句用于在循环中精确地控制代码的执行。

  • 其中,break 语句会立即退出循环, 强制继续执行循环后面的语句,相当于强制跳出了循环。
  • 而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行,相当于只是结束了本次循环。

请看下面的例子:

var num = 0; 
for (var i=1; i < 10; i++) { 
 if (i % 5 == 0) { 
 break; 
 } 
 num++; 
} 
alert(num); //输出4,因为在变量i等于5时,break语句执行,直接跳出了for循环,循环总共执行了 4 次

如果在这里把 break 替换为 continue 的话,则可以看到另一种结果:

var num = 0; 
for (var i=1; i < 10; i++) { 
 if (i % 5 == 0) { 
 continue; 
 } 
 num++; 
} 
alert(num);
/*输出8,因为在变量i等于5时,continue语句执行,立即结束了本次循环,从循环的顶部继续执行,直到 i 等于 10 时自然结束,而 num 的最终值之所以是 8,是因为 continue 语句导致它少递增了一次*/

break 和 continue 语句都可以与 label 语句联合使用,从而返回代码中特定的位置。这种联合 使用的情况多发生在循环嵌套的情况下,如下面的例子所示:

var num = 0; 
outermost: 
for (var i=0; i < 10; i++) { 
 for (var j=0; j < 10; j++) { 
 if (i == 5 && j == 5) { 
 break outermost; 
 } 
 num++; 
 } 
} 
alert(num); //55

在这个例子中,outermost 标签表示外部的 for 语句。如果每个循环正常执行 10 次,则 num++ 语句就会正常执行 100 次。换句话说,如果两个循环都自然结束,num 的值应该是 100。但内部循环中 的 break 语句带了一个参数:要返回到的标签。添加这个标签的结果将导致 break 语句不仅会退出内 部的 for 语句(即使用变量 j 的循环),而且也会退出外部的 for 语句(即使用变量 i 的循环)。为此, 当变量 i 和 j 都等于 5 时,num 的值正好是 55。同样,continue 语句也可以像这样与 label 语句联 用,如下面的例子所示:

var num = 0; 
outermost: 
for (var i=0; i < 10; i++) { 
 for (var j=0; j < 10; j++) { 
 if (i == 5 && j == 5) { 
 continue outermost; 
 } 
 num++; 
 } 
} 
alert(num); //95 

在这种情况下,continue 语句会强制继续执行循环——退出内部循环,执行外部循环。当 j 是 5 时,continue 语句执行,而这也就意味着内部循环少执行了 5 次,因此 num 的结果是 95。 虽然联用 break、continue 和 label 语句能够执行复杂的操作,但如果使用过度,也会给调试 带来麻烦。在此,我们建议如果使用 label 语句,一定要使用描述性的标签,同时不要嵌套过多的循环。

8 函数

8.1 函数的基本语法

function functionName(arg0, arg1,...,argN) { 
 statements 
} 

下面是一个示例

function sayHi(name, message) { 
 alert("Hello " + name + "," + message); 
} 

//调用 sayHi()函数的代码如下所示
sayHi("Nicholas", "how are you today?"); 
//警告框显示结果
//"Hello Nicholas,how are you today?"

8.2 return语句

① ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return 语句后跟要返回的值来实现返回值。请看下面的例子:

function sum(num1, num2) { 
 return num1 + num2; 
} 
//调用函数
var result = sum(5, 10); 

② 这个函数会在执行完 return 语句之后停止并立即退出。因此,位于 return 语句之后的任何代码都永远不会执行。例如:

function sum(num1, num2) { 
 return num1 + num2; 
 alert("Hello world"); // 永远不会执行
} 

③ 一个函数中也可以包含多个 return 语句,如下面这个例子中所示:

function diff(num1, num2) { 
 if (num1 < num2) { 
 return num2 - num1; 
 } else { 
 return num1 - num2; 
 } 
} 

return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined 值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。比如在下面这个例子中,就不会显示警告框

function sayHi(name, message) { 
 return; 
 alert("Hello " + name + "," + message); //永远不会调用
} 

推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候有不返回值,会给调试代码带来不便。

8.3 严格模式对函数有一些限制

  • 不能把函数命名为 eval 或 arguments;
  • 不能把参数命名为 eval 或 arguments;
  • 不能出现两个命名参数同名的情况。 如果发生以上情况,就会导致语法错误,代码无法执行。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值