超长长长 ECMAScript 笔记

ECMAScript

什么是Javascript?

  • Javascript简称JS,是前端开发的一门脚本语言(解释性语言)
  • 解释型语言:程序执行以前,不须要对代码进行编译,在运行时边解析边执行的语言。
  • 浏览器工做原理:

img

浏览器的主要组件为 (1.1):javascript

  1. 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其余显示的各个部分都属于用户界面。
  2. 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
  3. 呈现引擎 - 负责显示请求的内容。若是请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
  4. 网络 - 用于网络调用,好比 HTTP 请求。其接口与平台无关,并为全部平台提供底层实现。
  5. 用户界面后端 - 用于绘制基本的窗口小部件,好比组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操做系统的用户界面方法。
  6. JavaScript 解释器。用于解析和执行 JavaScript 代码。
  7. 数据存储。这是持久层。浏览器须要在硬盘上保存各类数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(可是轻便)的浏览器内数据库。
  • 编译型语言:程序执行以前,须要一个专门的编译过程,把程序编译成机器语言的文件,好比exe文件

做用

  • HTML:提供网页上显示的内容(结构)
  • CSS:美化网页(样式)
  • Javascript:控制网页行为(行为)

发展史

  • Javascript起源于Netscape公司的LiveScript语言
    • 1994年网景公司发布历史上第一个比较成熟的浏览器(Navigator 0.9),可是只能浏览不能交互
    • 1995年为了解决表单有效性验证就要与服务器进行屡次地往返交互问题,网景公司录用Brendan Eich(布兰登·艾奇),他在10天内开发出LiveScript语言
    • 在Netscape Navigator 2.0即将正式发布前,Netscape将LiveScript改名为Javascript,目的是为了蹭Java的热度
    • 因此Java和Javascript之间的关系就像老婆和老婆饼同样

参考文献css

组成

  • ECMAScript:JavaScript的语法标准html

    • ECMA是European Computer Manufacturers Association的缩写,即欧洲计算机制造商协会前端

    • ECMAScript是ECMA制定的脚本语言的标准,规定了一种脚本语言实现应该包含的基本内容html5

    • Javascript是脚本语言的一种,因此Javascript也必须遵照ECMAScript标准,包含ECMAScript标准中规定的基本内容java

  • DOM(Document Object Model):JavaScript操做网页上的元素(标签)的APInode

  • BOM(Browser Object Model):JavaScript操做浏览器的部分功能的API程序员

img

书写格式

1.CSS书写格式web

  • 行内样式: 写在标签内部数据库

  • 内嵌样式(内联样式) : 写在一对head-style标签中

  • 外链样式: 写在一个单独的.css文件中, 再导入进来

2.JavaScript书写格式

  • 行内样式: 写在标签内部

  • 内嵌样式(内联样式) : 写在一对head-script标签中

  • 外链样式: 写在一个单独的.js文件中, 再导入进来

3.JavaScript书写格式注意点

  • 不推荐直接将JavaScript代码书写到标签内部

  • 默认状况下浏览器会从上至下的解析网页, 因此若是将JavaScript写到一对head标签中, 而且须要经过JavaScript代码操做界面上的元素, 那么就不能直接书写JavaScript代码, 不然无效

    • 若是想将JavaScript写到一对head标签中, 而且须要在JavaScript代码中操做界面上的元素, 那么必须加上window.onload = function(){操做界面元素的JavaScript}
    window.onload = function () {
        var oDiv = document.querySelector("div");
        var text = oDiv.innerText;
        alert(text);
    }
    复制代码
    • window.onload的含义: 等到界面上全部的内容都加载完毕再执行{}中的代码
    • 因为默认状况下浏览器会从上至下的解析网页, 因此若是想经过JavaScript操做界面上的元素只须要等到元素被加载解析以后操做就能够了, 因此咱们还能够将JavaScript代码写到body结束标签的前面
  • 若是经过外链式导入.js文件, 而且须要在.js文件中操做界面上的元素, 并且是在head标签中导入的, 必须在.js文件中加上window.onload. 若是是在body结束标签前面导入的, 那么就不用添加window.onload

  • 若是须要在一对script标签中编写JavaScript代码, 那么就不能同时经过script标签再导入其它的.js文件, 不然书写的JavaScript代码无效

常见输出方式

1.经过弹窗的形式来输出

  • alert(须要输出的内容);

  • confirm(须要输出的内容);

  • prompt(须要输出的内容);

注意点:

若是须要输出的内容不是数字, 那么就必须经过单引号或者双引号括起来

在JavaScript中是严格区分大小写的, alert()和ALERT()不是一回事

在编写JavaScript代码的时候, 必定要记住每一句代码后面都须要添加一个分号, 而且这个分号必须是英文的分号

咱们会发现有时候不写分号程序也可以运行, 这里并非由于不须要分号, 而是浏览器自动帮助咱们添加了分号, 浏览器自动添加会消耗必定的性能, 而且有可能会添加错误

2.经过网页内容区域的形式来输出

  • document.write(须要输出的内容);

注意点:

若是须要输出的内容不是数字, 那么就必须经过单引号或者双引号括起来

3.经过开发者工具控制台的形式来输出

  • console.log(须要输出的内容); // 普通输出

  • console.warn(须要输出的内容); // 警告输出

  • console.error(须要输出的内容); // 错误输出

注意点:

若是须要输出的内容不是数字, 那么就必须经过单引号或者双引号括起来

常量

1.什么是常量?

常量表示一些固定不变的数据

现实生活中人的性别其实就能够看作是常量, 生下来是男孩一生都是男孩, 生下来是女孩一生都是女孩

2.JavaScript中常量的分类

  • 整型常量

    • 整型常量其实就是正数, 在JavaScript中随便写一个整数都是整型常量,如1 / 666 / 99
  • 实型常量

    • 实型常量其实就是小数, 在JavaScript中随便写一个小数都是实型常量,如3.14 6.66
  • 字符串常量

    • 字符串常量其实就是用单引号或者双引号括起来的内容, 咱们就称之为字符串常量,如 'a' / ‘abc' / "1" / "知播渔教育"
    • 注意点: 不管用单引号或者双引号括起来了多少个字符, 在JavaScript中都是字符串常量
  • 布尔常量

    • 布尔常量其实就是真或者假, 在JavaScript中经过true和false来表达
    • 在JavaScript中布尔常量只有两个取值, 真(true)或者假(false)
  • 自定义常量

    • 在ES6中新增的
    • const 常量名称 = 常量取值;

常量

1.什么是变量?

变量表示一些能够被修改的数据

在现实生活中超市的储物格就是变量, 在不一样的时间段里面, 储物格中存储的数据也不同

2.如何定义一个变量

在JavaScript中能够经过定义变量的方式来生成储物格, 也就是告诉浏览器, 咱们须要一块内存空间

var 变量名称;
复制代码

3.如何使用变量

使用变量就是往申请的那块内存空间中存储数据, 和获取存储的数据

3.1如何存储数据

变量名称 = 须要存储的数据;

能够将等号右边须要存储的数据放到等号左边变量申请的那块存储空间中

3.2如何获取存储在变量中的数据

变量名称

// 定义一个变量
var num;
// 往变量中存储数据
num = 123;
// 从变量中获取存储的数据
console.log(num);
复制代码

4.如何修改变量中存储的数据

在JavaScript中想要修改变量中存储的数据, 只须要再次给变量直接赋值便可

num = 666;
console.log(num);
复制代码

5.在JavaScript中第一次给变量赋值, 咱们称之为"变量的初始化"

var num;
num = 321; // "变量的初始化"
num = 888; // 不是"变量的初始化"
复制代码

6.若是一个变量没有进行初始化, 那么变量中存储的是什么呢?

在JavaScript中若是定义了一个变量,可是没有进行初始化, 那么变量中存储的是undefined

var num;
console.log(num);
复制代码

7.给变量初始化有不少种形式

  • 先定义变量, 再对变量进行初始化
var num;
复制代码
  • 能够在定义变量的同时对变量进行初始化
var value = 666; // 定义的同时初始化
复制代码

8.定义变量的其它格式

同时定义多个变量的格式,格式: var 变量名称1, 变量名称2, .... ;

var num, value; // 同时定义两个变量
复制代码

9.初始化变量的其它格式

若是在企业开发中对多个变量初始化的值都是同样的, 那么咱们能够经过:变量名称1 = 变量名称2 = 变量名称... = 初始化值;

num = value = 123; 
// 同时对num和value进行初始化, num和value中存储的数据都是123
复制代码

10.定义多个变量的同时给多个变量分别初始化

var num = 123, value = 666;
复制代码

11.注意点

  • 在JavaScript中变量之间是能够相互赋值的
var num;
var value;
num = 123;
value = num; // 将Num中的值拷贝一份给value
console.log(num);
console.log(value);
复制代码
  • 在JavaScript中若是定义了同名的变量, 那么后定义的变量会覆盖先定义的变量
var num = 666;
num = 888; 
// 若是num前面没有var, 那么就是修改变量中存储的值
var num = 888;
// 若是num前面有var, 那么就不是修改变量中存储的值, 而是从新定义一个新的变量
复制代码
  • 在老版本的标准的(ES6以前)JavaScript中能够先使用变量, 再定义变量, 并不会报错
  • 因为JavaScript是一门解释型的语言, 会边解析边执行, 浏览器在解析JavaScript代码以前还会进行一个操做"预解析(预处理)":将当前JavaScript代码中全部变量的定义和函数的定义放到全部代码的最前面
console.log(num);
var num = 123;

预处理以后的代码
var num;
console.log(num); // undefined
num = 123;
复制代码

12.ES6变量定义

为了解决老版本标准的的两个注意点:

  • 在JavaScript中若是定义了同名的变量, 那么后定义的变量会覆盖先定义的变量
  • 在老版本的标准的(ES6以前)JavaScript中能够先使用变量, 再定义变量, 并不会报错在ES6中就推出了一种新的定义变量的方式
格式:
ES6以前: var 变量名称;
ES6开始: let 变量名称;

// 定义一个变量
let num;
// 给变量初始化
num = 666;
// 取出存储的数据
console.log(num);
// 修改变量中存储的数据
num = 888;
// 取出存储的数据
console.log(num);

// var num = 123;
// var num = 888;
let num = 123;
let num = 888; //会明确的报错
console.log(num);

console.log(num);
var num = 123;   undefined
console.log(num);
let num = 123;   报错
复制代码

关键字和保留字

什么是关键字?

  • 被JavaScript语言赋予了特殊含义的单词
  • 关键字在开发工具中会显示特殊颜色
  • 关键字不能用做变量名、函数名等
  • 关键字严格区分大小写, var和Var前者是关键字, 后者不是
  • 只须要记住一点: 在JavaScript中全部的关键字都是小写的
关键字
break do instanceof typeof
case else new var
catch finally return void
continue for switch while
default if throw delete
in try function this
with debugger false true
null

什么是保留字?

  • JavaScript预留的关键字,他们虽然如今没有做为关键字,但在之后的升级版本中有可能做为关键字
保留字
class enum extends super
const export import implements
let private public yield
interface package protected static

标识符

什么是标识符?

  • 从字面上理解就是用来标识某些东西的符号,标识的目的就是为了将这些东西区分开来
  • 其实标识符的做用就跟人的名字差很少,为了区分每一个人,就在每一个人出生时起了名字
    • 平常生活中乔丹、刘德华、吴京这些都是标识符
    • 在编程中标识符就是程序员本身在程序中起的一些名字
    • 例如定义变量时的变量名就是一个标识符

命名规则(必须遵照):

  • 只能由26个英文字母的大小写、10个阿拉伯数字0~九、下划线_、美圆符号$组成
  • 不能以数字开头
  • 严格区分大小写,好比test和Test是2个不一样的标识符
  • 不可使用关键字、保留字做为标识符
  • JS底层保存标识符其实是采用Unicode编码,因此理论上讲,全部的utf-8中含有的内容均可以做为标识符

命名规范(建议遵照):

  • 见名知意,变量的名称要有意义(有利于提升阅读性)
  • 驼峰命名法,首字母小写,第二个单词的首字母大写(有利于提升阅读性)

注释

1.什么是JS的注释?

和HTML/CSS的注释同样, 都是用来注解解释某一段程序的含义的, 都是用来提高代码的阅读性的, 都是为了方便程序员之间沟通的

2.JS注释的格式

  • 单行注释: // 被注释的内容
    • 注意点: 单行注释的有效范围是从第二个斜杠开始一直直到这一行的末尾, 也就是被注释的内容不能换行
  • 多行注释: /* 被注释的内容 */
    • 注意点: 多行注释的有效范围是从第一颗星开始直到第二颗星结束, 也就是说被注释的内容能够换行的

3.JS中注释的嵌套规则

  • 单行注释能够嵌套单行注释 / 多行注释, 可是必须在一行(多余)
  • 多行注释能够嵌套单行注释(多余)
  • 多行注释不能够嵌套多行注释
/*
被注释的内容
/*
*/
*/
复制代码

数据类型

1.什么是数据?

  • 生活中无时无刻都在跟数据打交道,如人的体重、身高、收入、性别等数据
  • 在咱们使用计算机的过程当中,也会接触到各类各样的数据,如文档数据、图片数据、视频数据等

2.数据分类

  • 静态数据

    • 静态数据是指一些永久性的数据,通常存储在硬盘中。硬盘的存储空间通常都比较大,如今普通计算机的硬盘都有500G左右,所以硬盘中能够存放一些比较大的文件
    • 存储时长:计算机关闭以后再开启,这些数据都还在,只要你不主动删掉或硬盘没坏,这些数据永远都在
    • 哪些是静态数据:通常是以文件形式存储在硬盘上,好比文档、图片、视频等
  • 动态数据

    • 动态数据指在程序运行过程当中,动态产生的临时数据,通常存储在内存中,内存的存储空间通常比较小,如今普通计算机的内存只有8G左右,所以要谨慎使用内存,不要占用太多的内存空间
    • 存储时长:计算机关闭后,这些临时数据就会被清除
    • 哪些是动态数据:当运行某个程序(软件)时,整个程序就会被加载到内存中,在程序运行过程当中,会产生各类各样的临时数据,这些临时数据都是存储在内存中的,当程序中止运行或者计算机被强制关闭时,这个程序产生的全部临时数据都会被清除
  • 既然硬盘的存储空间这么大,为什么不把全部的应用程序加载到硬盘中去执行呢?

    • 主要缘由是内存的访问速度比硬盘快N倍
  • 静态数据和动态数据的相互转换:也就是从磁盘加载到内存

  • 动态数据和静态数据的相互转换:也就是从内存保存到磁盘

  • 数据的计量单位

    • 无论是静态仍是动态数据,都是0和1组成
    • 数据越大,包含的0和1越多

3.数据类型概述

做为程序员最关心的是内存中的动态数据,由于咱们写的程序就是在内存中的,程序在运行过程当中会产生各类各样的临时数据,为了方便数据的运算和操做,JavaScript对这些数据进行了分类,提供了丰富的数据类型

  • 基本数据类型

    • Number 数值类型

      在JavaScript中不管是整数仍是小数都是属于数值类型的

    • String 字符串类型

      在JavaScript中不管是经过单引号仍是经过双引号括起来的内容都是属于字符串类型的

    • Boolean 布尔类型

      在JavaScript中布尔类型比较特殊, 只有两个取值true/false

    • Undefined 未定义类型

      在JavaScript中未定义类型比较特殊, 只有一个取值undefined

    • Null 空类型

  • 引用数据类型

    • Object 对象类型
  • 在JavaScript中为了方便咱们检测某一种数据是属于哪种数据类型的, JavaScript提供了一个名称叫作typeof的操做符

    • 格式: typeof 须要检测的数据;
let res = typeof 123;
console.log(res);   >>number

let num;
console.log(num);
// 以上代码的含义是输出num变量中保存的数据
// 因为没有给num这个变量进行初始化, 因此这个变量中保存的数据默认是undefined(取值)

let res = typeof num;
console.log(res);
// 利用typeof检查num中保存的数据是什么类型的
// 也就是说利用typeof检查undefined是属于什么类型的(数据类型)
复制代码

强制类型转换

1.转换为字符串类型

  • toString()
    • Number和Boolean能够用这个转换
    • 不会影响原变量
    • null和undefined这两个值没有toString()方法
    • 变量名称.toString()的方式前面不能是常量, 由于常量不能被改变
let value = 123;
console.log(value); 
// 在谷歌浏览器的控制台中若是是Number类型是蓝色的

// 如下代码的含义: 将value变量中存储的数据拷贝一份, 而后将拷贝的数据转换为字符串以后返回
let str = value.toString();
console.log(str); 
// 在谷歌浏览器的控制台中若是是String类型是灰色的

// 注意点: 不能使用常量直接调用toString方法, 由于常量是不能改变的
let str2 = 123.toString();
复制代码
  • String(常量or变量);
    • 可是对于null和undefined,它会将 null 直接转换为 "null"。将 undefined 直接转换为 "undefined"
    • 对于Number和Boolean而言,实际上就是调用toString()方法
    • 能够用常量由于是根据传入的值从新生成一个新的值, 并非修改原有的值
  • 变量or常量 + "" / 变量or常量 + ''
    • 本质其实就是调用String()函数
let str = 123 + '';
console.log(str);  >>'123'
console.log(typeof str);   >>String
复制代码

2.转换为数值类型

  • String >> 数值

    • 若是字符串中都是数值, 那么就正常转换
    • 若是字符串是一个空串" " / "", 那么转换以后是0
    • 若是字符串中不只仅是数字, 那么转换以后是NaN
  • Boolean >> 数值

    • true转换以后是1
    • false转换以后是0
  • undefined >> 数值

  • 转换以后是NaN

  • null >> 数值

  • 转换以后是0

  • 空字符串/false/null转换以后都是0,字符串中不只仅是数字/undefined转换以后是NaN,其它的正常转换

  • 转换方法

    • Number(常量or变量);
    let str = "123";
    console.log(str);   >>"123"
    console.log(typeof str);   >>String
    let num = Number(str);   
    console.log(num);   >>123
    console.log(typeof num);   >>Number
    复制代码
    • 经过数学运算中的+号和-号来转换

      虽然经过+/-均可以将其它类型转换为数值类型, 可是-会改变数值的正负性

      底层本质上就是调用了Number函数

    let str = "123";
    let num = +str;
    let num = -str;
    复制代码
    • parseInt(须要转换的字符串)/parseFloat(须要转换的字符串)

      从左至右的提取数值, 一旦遇到非数值就会当即中止,若是尚未提取到数值, 那么就返回NaN

      parseInt/parseFloat都会将传入的数据当作字符串来处理

    let str = "3.14px";
    let num = parseFloat(str);
    console.log(num);   >>3.14
    console.log(typeof num);   >>Number
    
    let value = true;
    let num = parseInt(value); // parseInt("true");
    console.log(num);   >>NaN
    console.log(typeof num);   >>Number
    复制代码

3.转换为布尔类型

  • String >> 布尔
    • 字符串中有内容为true, 字符串中没有内容为false
  • Number >> 布尔
    • 只有数值是0才会转换为false, 其它的都会转换为true
    • 若是是NaN也会转换为false(在JavaScript中NaN属于Number类型)
  • undefined >> 布尔
    • 转换为false
  • null >> 布尔
    • 转换为false
  • 空字符串/0/NaN/undefined/null 会转换成false, 其它的都是true
  • 方法:Boolean(常量or变量)
let num = NaN;
console.log(typeof num);   >>number
let flag = Boolean(num);   
console.log(flag);   >>false
console.log(typeof flag);   >>boolean
复制代码

运算符

简介

  • 运算符也叫操做符。经过运算符能够对一个或多个值进行运算,并获取运算结果。

  • 好比:typeof 就是运算符,能够来得到一个值的类型。它会将该值的类型以字符串的形式返回

  • +*/( 都是运算符,而(3+5)/2则是表达式

优先级和结合性

  • 从左往右先乘除后加减,有括号先算括号
优先级 运算符 说明 结合性
1 [].() 字段访问、数组索引、函数调用和表达式分组 从左向右
2 ++ -- -~!delete new typeof void 一元运算符、返回数据类型、对象建立、未定 义的值 从右向左
3 *、/、% 相乘、相除、求余数 从左向右
4 +、- 相加、相减、字符串串联 从左向右
5 <<、>>、>>> 左位移、右位移、无符号右移 从左向右
6 <、<=、>、>=、instanceof 小于、小于或等于、大于、大于或等于、是否 为特定类的实例 从左向右
7 ==、!=、===、!== 相等、不相等、全等,不全等 从左向右
8 & 按位“与” 从左向右
9 ^ 按位“异或” 从左向右
10 | 按位“或” 从左向右
11 && 短路与(逻辑“与”) 从左向右
12 || 短路或(逻辑“或”) 从左向右
13 ?: 条件运算符 从右向左
14 =、+=、-=、*=、/=、%=、&=、|=、^=、<、<=、>、>=、>>= 混合赋值运算符 从右向左
15 , 多个计算 按优先级计算,而后从右向左
算术运算符

1.什么是算术运算符?

算术运算符
+ 加、字符串链接
-
*
/
% 获取余数(取余)
() 括号,优先级

2.算术运算符的优先级和结合性

  • * / % 优先级要高于 + -
  • 不管是+ - * / %都是左结合性(从左至右计算)
let result = 10 % 4;
console.log(result);   >>2
复制代码

3.注意点

  • 当对非Number类型的值进行运算,会将这些值转换为Number而后再运算
result1 = true + 1;  // 2 = 1+ 1
result2 = true + false; // 1 = 1+ 0
result3 = 1 + null; // 1 = 1+ 0
result4 = 100 - '1' // 99
复制代码
  • 任何值和NaN作运算的结果都是NaN
  • 任何的值和字符串作加法运算,都会先转换为字符串,而后再作拼串操做
result1 = 1 + 2 + '3'  // 33
result2 = '1' + 2 + 3; // 123
复制代码
  • 任何值作-*/运算时都会自动转换为Number
  • 取模(取余)运算:m%n = 余数
    • 若是m>n的, 那么就正常取余
    • 若是m<n的, 那么结果就是m
    • 若是n是0, 那么结果就是NaN
    • 取余运算结果的正负性, 取决于m而不是n
let res = -10 % 3;   // -1
复制代码
赋值运算符

1.什么是赋值运算符?

  • 就是将等号右边的值存储到等号左边的变量中
  • 简单类型:=;复杂类型:+= -= *= /= %=

2.优先级和结合性

  • 赋值运算符的优先级低于算数运算符
  • 右结合性(从右至左的计算)
  • 左边只能放变量, 不能放常量
//因为算数运算符的优先级高于赋值运算符因此会先计算1 + 1, 而后再赋值给res
let res = 1 + 1;

// 因为赋值运算符的结合性是右结合性, 因此会先将3赋值给num2, 而后再将Num2中的值赋值给num1
let num1, num2;
num1 = num2 = 3;
复制代码
自增自减运算符

1.什么是自增自减运算符?

  • 自增运算符: ++
  • 自减运算符: --
  • 用在变量中,不能用在常量/表达式中

2.自增

  • 自增分红两种:a++++a
  • 对于一个变量自增之后,原变量的值会当即自增1。也就是说,不管是 a++ 仍是++a,都会当即使原变量的值自增1。
  • 要注意的是**:a是变量,而a++++a是**表达式
  • a++的值等于原变量的值(a自增前的值)
  • ++a的值等于新值 (a自增后的值)

3.自减:同上

var n1=10;
var n2=20;

var n = n1++; //n1 = 11 n1++ = 10

console.log('n='+n);  // 10
console.log('n1='+n1); //11

n = ++n1 //n1 = 12 ++n1 =12
console.log('n='+n); //12
console.log('n1='+n1); //12

n = n2--;// n2=19 n2--=20
console.log('n='+n); //20
console.log('n2='+n2); //19

n = --n2; //n2=18 --n2 = 18
console.log('n='+n); //18
console.log('n2='+n2); //18
复制代码
关系运算符
  • 经过关系运算符能够比较两个值之间的大小关系,若是关系成立它会返回true,若是关系不成立则返回false
  • 左结合性(从左至右的运算)
  • > < >= <= 的优先级高于 == != === !==
//正式由于关系运算符是左结合性, 因此不能利用关系运算符来判断区间
let res = 10 > 5 > 3;  // true > 3; 1 > 3
let res = 10 <= 25 <= 20;  // true <= 20 1 <= 20
复制代码
关系运算符
> 大于
< 小于
>= 大于或等于
<= 小于或等于
== 等于
=== 全等于
!= 不等于
!== 不全等于
  • 对于非数值类型的数据, 会先转换成数值类型, 再进行判断
let res= 1 > true; // 1 > 1
let res= 1 > false;  // 1 > 0
let res= 1 > null;  // 1 > 0
let res= 1 > '10';  // 1 > 10
复制代码
  • 任何数据和NaN进行比较, 返回值都是false
console.log(10 <= 'hello'); //false
复制代码
  • 若是参与比较的都是字符串类型, 那么不会转换成数值类型再比较, 而是直接比较字符对应的Unicode编码
    • 比较字符编码时,是一位一位进行比较。若是两位同样,则比较下一位,因此借用它能够来对英文进行排序
    • 所以当咱们在比较两个字符串型的数字时,必定必定要先转型,好比 parseInt()
// 比较两个字符串时,比较的是字符串的字符编码,因此可能会获得不可预期的结果
console.log('56'>'123'); //true

console.log('b' > 'a'); // 0062 > 0061
复制代码

unicode编码转换地址

  • ==符号
    • 这个符号并不严谨,会将不一样类型的东西,转为相同类型进行比较
    • 只会判断取值
    • undefined 衍生自 null,因此这两个值作相等判断时,会返回true
    • NaN不和任何值相等,包括他自己
    • 能够经过isNaN()函数来判断一个值是不是NaN
console.log('6'== 6);  >>true
console.log(true == '1');  >>true
console.log(0 == -0);  >>true
console.log(null == 0);  >>false

console.log(undefined == null);  >>true

console.log(NaN == NaN);  >>false

console.log(isNaN(b));  >>false
复制代码
  • ===符号
    • 若是要保证彻底等于,咱们就要用三个等号===全等不会作类型转换
    • 会同时判断取值和数据类型
    • ==的反面是!====的反面是!==
console.log('6' === 6);  >>false
console.log(6 === 6);  >>true
复制代码
逻辑运算符
  • 分类

    • && 与(且):两个都为真,结果才为真。

    • || 或:只要有一个是真,结果就是真。

    • ! 非:对一个布尔值进行取反。

  • 注意事项

  • 左结合性(从左至右的运算),&&的优先级高于||

  • 能参与逻辑运算的,都是布尔值

  • 逻辑短路

    • JS中的&&属于短路的与,若是第一个值为false,则不会看第二个值
    • JS中的||属于短路的或,若是第一个值为true,则不会看第二个值
    true && alert('hhh');  //hhh
    true || alert('hhh');  //true
    复制代码
  • 非布尔值运算

    • 若是对非布尔值进行逻辑运算,则会先将其转换为布尔值,而后再操做, 但返回结果是原值
    var result = 5 && 6;  //true && true
    console.log(result);  //6
    复制代码
    • 与运算的返回结果:(以两个非布尔值的运算为例)
    • 若是第一个值为true,则必然返回第二个值(因此说,若是全部的值都为true,则返回的是最后一个值)
    • 若是第一个值为false,则直接返回第一个值
    • 或运算的返回结果:(以两个非布尔值的运算为例)
    • 若是第一个值为true,则直接返回第一个值
    • 若是第一个值为false,则返回第二个值
  • 实际开发(作容错处理 )

// 当成功调用一个接口后,返回的数据为 result 对象。这个时候,咱们用变量 a 来接收 result 里的图片资源。一般的写法是这样的:(这里我只是举个例子)

if (result.resultCode == 0) {
		var a = result && result.data && result.data.imgUrl || 'http://img.smyhvae.com/20160401_01.jpg';
}

// 上方代码的意思是,获取返回结果中的result.data.imgUrl这个图片资源;若是返回结果中没有 result.data.imgUrl 这个字段,就用 http://img.smyhvae.com/20160401_01.jpg 做为兜底图片。这种写法,在实际开发中常常用到。
复制代码
逗号运算符
  • 逗号运算符 ,
    • 在JavaScript中逗号运算符通常用于简化代码
    • 左结合性(从左至右的运算)
    • 优先级是全部运算符中最低的
    • 逗号运算符也是一个运算符, 因此也有运算符结果逗号运算符的运算符结果就是最后一个表达式的结果:表达式1, 表达式2, 表达式3, ....;
// 利用逗号运算符同时定义多个变量
let a,b;
// 利用逗号运算符同时给多个变量赋值
a = 10,b = 5;

let res = ((1+1),(2+2),(3+3));
console.log(res);  //6
复制代码
三元运算符 / 条件运算符
  • 语法: 条件表达式 ? 语句1 : 语句2;
    • 若是该值为true,则执行语句1,并返回执行结果
    • 若是该值为false,则执行语句2,并返回执行结果

流程控制语句

基本概念

  • 默认状况下,浏览器会按书写从上值下顺序执行程序中的每一行代码,可是这并不能知足咱们全部的开发需求
  • 为了方便咱们控制程序的运行流程,JS提供了顺序结构、选择结构(if/switch)、循环结构(while/for)三种语句
if语句

1.条件判断语句

  • 当条件表达式为真的时候就会执行{}中全部的代码, 而且只会执行一次
if(条件表达式){
	条件为真时执行语句;
}

let age = 19;
if(age >= 18){
	console.log('开网卡');
}
console.log('卖饮料');
复制代码

3.条件分支语句

if(条件表达式){
	条件为真时执行语句;
}else{
	条件为假时执行语句;
}

if(条件表达式1){
	条件1为真时执行语句;
}else if(条件表达式2){
	条件为1不知足时执行语句;
}else if(条件表达式3){
	条件12不知足时执行语句;
}else{
	条件123都布知足时执行语句;
}
复制代码

3.注意点

  • 对于非布尔类型的数据, 会先转换成布尔类型再判断
if(null){
	console.log('a');
}
console.log('b');
>>b
复制代码
  • 对于== /===判断, 将常量写在前面(避免写成赋值=而出错)
let num = 10;
if(5 == num){
	console.log('a');
}
复制代码
  • if / else if / else后面的大括号均可以省略, 可是省略以后只有紧随其后的语句受到控制
if(false)
	console.log('a');
	console.log('b');  >>b
复制代码
  • 在JavaScript中分号(;)也是一条语句(空语句)
if(false);
{
	console.log('a');
	console.log('b');
}
>>;
复制代码
  • if选择结构能够嵌套使用
if(true){
	if(false){
		console.log('a');
	}else{
		console.log('b');
	}
}else{
	if(false){
		console.log('c');
	}else{
		console.log('d');
	}
}
复制代码
  • 当if选择结构省略大括号时, else if/else会自动和距离最近没有被使用的if匹配
if(0)
	if(1)
		console.log('a');
	else
		console.log('b');
else
	if(1)
		console.log('c');
	else
		console.log('d');
复制代码
在企业开发中, 若是条件知足以后只有一句代码须要执行, 那么就使用三元运算符
在企业开发中, 若是条件知足以后有多句代码须要执行, 那么就使用选择结构

let num = prompt('请输入');
num % 2 === 0 ? alert('yes') : alert('no');

if(num % 2 === 0){
	alert('yes');
	num = 666;
}else{
	alert('no');
	num = 123;
}
console.log(num);
复制代码
switch语句
switch(表达式){
	case 表达式1:
		语句1;
		break;
	case 表达式2:
		语句2;
		break;
	......
	default:
		前面全部case都不匹配执行的代码;
		break;
}
复制代码
  • 会从上至下的依次判断每个case是否和()中表达式的结果相等, 若是相等就执行对应case后面的代码, 若是前面全部的case都不匹配, 那么就会执行default后面的代码

  • 而且全部的case和default只有一个会被执行, 而且只会被执行一次

  • 注意点:

    • case判断的是=== , 而不是 ==
    let num = 123;
    switch (num){
    	case'123':
    		console.log('字符串123');
    		break;
    	case 123:
    		console.log('数值123');
    		beak;
    	default:
    		console.log('other');
    		break;
    }
    复制代码
    • ()中能够是常量也能够是变量还能够是表达式
    // let num = 123;
    // switch (num) { // 变量
    // switch (123) { // 常量
    switch (122 + 1) { // 表达式
        case 123:
            console.log("数值的123");
            break;
        default:
            console.log("Other");
            break;
    }
    复制代码
    • case后面能够是常量也能够是变量还能够是表达式
    let num = 123;
    switch (123) {
        // case 123: // 常量
        // case num: // 变量
        case 100 + 23: // 变量
            console.log("数值的123");
            break;
        default:
            console.log("Other");
            break;
    }
    复制代码
    • break的做用是当即结束整个switch语句,在switch语句中一旦case或者default被匹配, 那么其它的case和default都会失效
    let num = 1;
    switch (num) {
        case 1:
            console.log("1");
            break;
        case 2:
            console.log("2");
            break;
        default:
            console.log("Other");
            break;
    }
    复制代码
    • default不必定要写在最后,switch中的default不管放到什么位置, 都会等到全部case都不匹配再执行
    let num = 7;
    switch (num) {
        // default:
        // console.log("Other");
        // break;
        case 1:
            console.log("1");
            break;
        default:
            console.log("Other");
            break;
        case 2:
            console.log("2");
            break;
    }
    复制代码
    • 和if/else中的else同样, default也能够省略
    let num = 7;
    switch (num) {
        case 1:
            console.log("1");
            break;
        case 2:
            console.log("2");
            break;
    }
    复制代码

if和switch如何选择

  • 在企业开发中若是是对区间进行判断, 那么建议使用if
  • 在企业开发中若是是对几个固定的值的判断, 那么建议使用switch
  • 原则: 能用if就用if
需求: 要求判断一个数是不是大于100的数

let num = 20;
if(num > 100){
	alert('大于100的数');
}else{
	alert('不大于100的数');
}
复制代码

循环语句

while循环
1.if的格式
if(条件表达式){
    条件知足执行的语句;
}
if的特色: 只有条件表达式为真才会执行后面{}中的代码,大括号中的代码只会被执行一次
                  
2.while的格式
while(条件表达式){
    条件知足执行的语句;
}
while的特色: 只有条件表达式为真才会执行后面{}中的代码,大括号中的代码有可能会被执行屡次                     
复制代码
  • 执行流程
    • 首先会判断条件表达式是否为真, 若是为真就执行后面{}中的代码
    • 执行完后面{}中的代码, 会再次判断条件表达式是否还为真
    • 若是条件表达式还为真, 那么会再次执行后面{}中的代码
    • 直到条件表达式不为真为止
let num = 1;
while(num <= 10){
	console.log('发射子弹' + num);
	num++;
}
复制代码
  • 注意点

    • 什么是死循环?
      • 条件表达式永远为真的循环结构
    while(true){
    	console.log('123');
    }
    复制代码
    • 什么是循环体?
      • 循环结构后面的{}
    • 和if同样对于非Boolean类型的值, 会先转换为Boolean类型再判断
    while(1){
    	console.log('被执行了');
    }
    复制代码
    • 和if同样while 后若是只有一条语句它能够省略大括号,和if同样若是省略了后面的{}, 那么只有紧随其后的那条语句受到控制

    • 和if同样, 不能在()后面写分号 ;

    • 最简单死循环写法

    while(1);
    复制代码
do...while循环
while循环的格式
while(条件表达式){
    须要重复执行的代码;
}
while循环的特色: 只有条件表达式为真, 才会执行循环体

dowhile循环的格式
do{
    须要重复执行的代码;
}while(条件表达式);
dowhile循环的特色: 不管条件表达式是否为真, 循环体都会被执行一次
复制代码

while和do while如何选择

  • 在企业开发中大部分状况下while循环和dowhile循环是能够互换的
  • 在企业开发中若是循环体中的代码不管如何都须要先执行一次, 那么建议使用dowhile循环
  • 在企业开发中其它的状况都建议使用while循环
需求: 要求用户输入密码, 判断输入密码是否正确(假设正确密码是123456)

let pwd = prompt('请输入密码');
while(pwd !== '123456'){
	pwd = prompt('请输入密码');
}
alert('欢迎回来');

let pwd = -1;
do{
	pwd = prompt('请输入密码');
}while(pwd !== '123456');
alert('欢迎回来');
复制代码
变量做用域

1.在JavaScript中定义变量有两种方式

  • ES6以前: var 变量名称;
  • ES6开始: let 变量名称;

2.两种定义变量方式的区别

  • var

    • 能够重复定义同名的变量, 而且不会报错, 而且后定义的会覆盖先定义的
    var num=123;
    var num=456;
    console.log(num);  >>456
    复制代码
    • 能够先使用后定义(预解析)
    console.log(num);
    var num=123;  >>undefined
    复制代码
  • let

    • "相同做用域内"不能够重复定义同名的变量
    • 在不一样的做用域范围内, 是能够出现同名的变量的
    let num=123;
    let num=456;  >>报错
    
    //只要出现了let, 在相同的做用域内, 就不能出现同名的变量
    let num = 123;
    var num = 456; // 会报错
    
    var num = 123;
    let num = 456; // 会报错
    
    {
    	let num=123;
    	{
    		let num=456;  // 不会报错
    	}
    }
    复制代码
    • 不能够先使用再定义, 由于浏览器不会对let定义的变量进行预解析
    console.log(num);
    let num=123;  >>报错
    复制代码

3.什么是全局变量和局部变量

  • 全局变量就是定义在{}外面的变量
  • 局部变量就是定义在{}里面的变量

4.区别

  • 全局变量

    • 有效范围是从定义变量的那一行开始直到文件的末尾均可以使用
    • 不管是经过var仍是经过let定义的全局变量, 都是从定义的那一行到文件末尾均可以使用
  • 局部变量,

    • 有效范围是从定义变量的那一行开始直到大括号结束为止(只有在大括号中才能使用)
    • 若是是经过var定义的局部变量, 仍是一个全局变量, 后续均可以被使用
    {
    	var num=123;
    }
    console.log(num);  //不会报错
    复制代码
    • 若是是经过let定义的局部变量, 那么这个变量只能在当前定义变量的{}中使用
    {
    	let num=123;
    }
    console.log(num);  //会报错
    复制代码
for循环
1			2/5			4/7
for(初始化表达式;条件表达式;循环后增量表达式){
	3/6
	须要重复执行的代码;
}
复制代码

1.特色

  • 和while循环的特色同样, 只有条件表达式为真, 才会执行循环体

2.执行流程

  • 首先会执行初始化表达式, 而且只会执行一次
  • 判断条件表达式是否为真, 若是条件表达式为真, 就执行循环体
  • 执行完循环体就会执行循环后增量表达式
  • 直到条件表达式不为真为止
for(let num = 1;num <= 10;num++){
	console.log('发送子弹'+num);
}
复制代码

3.注意点

  • 在for循环中"初始化表达式""条件表达式""循环后增量表达式"均可以不写,若是不写就至关于while(1);
  • while循环不能省略条件表达式,for循环是能够省略条件表达式的, 默认就是真
while(){
	console.log('123');
}
>>报错

for(;;){
	console.log('123');
}
>>123
复制代码
  • 其它注意点和while循环同样

for和while如何选择

  • 若是是while循环, 在循环结束以后还可使用用于控制循环结束的变量
  • 若是是for循环, 在循环结束以后可让外界使用, 也能够不让外界使用(取决于定义变量let的位置)
  • 在企业开发中因为for循环比while循环要灵活, 因此能用for循环就用for循环
关键字

break

  • break能够用来退出switch语句或整个循环语句(循环语句包括for、while,不包括if。if里不能用 break 和 continue,不然会报错)
  • break会当即终止离它最近的那个循环语句
  • 后面不能编写任何的语句, 由于永远执行不到
  • 若是在循环嵌套的结构中, break结束的是当前所在的循环结构
for(let i = 0; i < 5; i++){
    console.log("外面的循环结构" + i);
    for(let j = 0; j < 5; j++){
        console.log("里面的循环结构-----" + j);
        break;
    }
}
复制代码

continue

  • 只能用于循环结构,默认只会离他最近的循环起做用。
  • 做用是跳过本次循环, 进入下一次循环
  • 后面和break同样, 不能编写其它的代码, 由于执行够不到
  • 若是出如今循环嵌套结构中, 只会跳过当前所在的循环
for(let num = 1; num <= 10; num++){
    if(num === 1){
        continue;
        console.log("continue后面的代码"); // 永远执行不到
    }
console.log("发射子弹" + num);
复制代码
//循环嵌套规律

// 在循环嵌套中外循环控制的是行数, 内循环控制的是列数
for(let j = 0;j < 3;j++){
	for(let i = 0;i < 4;i++){
		document.write('*');
	}
	document.write('<br>');
}

// 规律: 若是尖尖朝下, 那么只须要修改内循环的初始化表达式为外循环初始化表达式的变量便可
for(let i = 0;i < 5;i++){
	for(let j = i;j < 5;j++){
		document.write('*');
	}
	document.write('<br>');
}

// 规律: 若是尖尖朝上, 那么只须要修改内循环的条件表达式为外循环初始化表达式的变量便可
for(let i = 0;i < 5;i++){
	for(let j = 0;j <= i;j++){
		document.write('*');
	}
	document.write('<br>');
}
复制代码

数组

1.什么是数组?

  • 数组就是专门用于存储一组数据的,是引用数据类型(对象类型)

2.如何建立一个数组?

let 变量名称 = new Array(size);
复制代码

3.如何操做数据

  • 如何往数组中存储数据
变量名称[索引号] = 须要存储的数据;
复制代码
  • 如何从数组中获取存储的数据
变量名称[索引号];
复制代码
let arr = new Array(3);
arr[0] = 'zs';
console.log(arr[0]);
复制代码

4.注意点

  • 和其它编程语言不一样, 若是数组对应的索引中没有存储数据, 默认存储的就是undefined,其它编程语言中默认保存的是垃圾数据或者0
let arr = new Array(3);
console.log(arr[0]);  >>undefined
复制代码
  • 和其它编程语言不一样, JavaScript中访问了数组中不存在的索引不会报错, 会返回undefined,其它编程语言一旦超出索引范围就会报错或者返回脏数据
let arr = new Array(3);
console.log(arr[666]);  >>undefined
复制代码
  • 和其它编程语言不一样, 当JavaScript中数组的存储空间不够时数组会自动扩容,其它编程语言中数组的大小是固定的
let arr = new Array(3);
arr[0] = "lnj";
arr[1] = "zs";
arr[2] = "ls";
arr[3] = "it666";
console.log(arr);  >>['lnj','zs','ls','it666']
复制代码
  • 和其它编程语言不一样, JavaScript的数组能够存储不一样类型数据,在其它编程语言中数组只能存储相同类型数据(要么所有都是字符串, 要么所有都是数值等)
let arr = new Array(4);
arr[0] = 123;
arr[1] = "123";
arr[2] = true;
arr[3] = null;
console.log(arr);  >>[123,'123',true,null]
复制代码
  • 和其它编程语言不一样, JavaScript中数组分配的存储空间不必定是连续的

    • 其它语言数组分配的存储空间都是连续的, JavaScript数组是采用"哈希映射"方式分配存储空间
    • 什么是哈希映射? 比如字典能够经过偏旁部首找到对应汉字, 咱们能够经过索引找到对应空间
  • 在浏览器中各大浏览器也对数组分配存储空间进行了优化

    • 若是存储的都是相同类型的数据, 那么会尽可能分配连续的存储空间
    • 若是存储的不是相同的数据类型, 那么就不会分配连续的存储空间
  • 建立数组的其它方式

    • 经过构造函数建立数组
    let 变量名称 = new Array(size);  //建立一个指定大小数组
    let 变量名称 = new Array();  //建立一个空数组
    let 变量名称 = new Array(data1,data2,...);  
    //建立一个带数据的数组
    复制代码
    • 经过字面量建立数组
    let 变量名称 = [];  //建立一个空数组
    let 变量名称 = [data1,data2,...];  //建立一个带数据的数组
    复制代码
遍历
  • 数组的遍历就是依次取出数组中存储的全部数据
let arr=['a','b',c];
for(let i=0; i<arr.length; i++){
	console.log(arr[i]);
}
复制代码
解构赋值
  • ES6中新增的一种赋值方式
  • 在数组的解构赋值中, 等号左边的格式必须和等号右边的格式如出一辙, 才能彻底解构
let [a,b,c] = [1,3,5];
let [a,b,[c,d]] = [1,3,[2,4]];
复制代码
  • 左边的个数能够和右边的个数不同
let [a,b] = [1,3,5];
console.log(a);
console.log(b);  >>1,3
复制代码
  • 右边的个数能够和左边的个数不同
let [a,b,c] = [1];
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);  >>1,undefined,undefined
复制代码
  • 若是右边的个数和左边的个数不同, 那么咱们能够给左边指定默认值
let [a, b = 666, c = 888] = [1];
console.log("a = " + a);
console.log("b = " + b);
console.log("c = " + c);  >>1,666,888
复制代码
  • 若是左边的个数和右边的个数不同, 那么若是设置默认值会被覆盖
let [a, b = 666] = [1, 3, 5];
console.log("a = " + a);
console.log("b = " + b);  >>1,3
复制代码
  • 还可使用ES6中新增的扩展运算符来打包剩余的数据,可是扩展运算符只能写在最后
ES6中新增的扩展运算符: ...

let [a, ...b] = [1,3,5];
console.log(a);  >>1
console.log(b);  >>[3,5]
复制代码
增删改查

1.查

let arr = ['a','b','c'];
需求: 获取数组中索引为1的那个数据 
console.log(arr[1]);
复制代码

2.改

  • splice
//需求: 将索引为1的数据修改成m
arr[1] = 'm';
console.log(arr);

//需求: 将索引为1的数据修改成d, 索引为2的修改成e
arr.splice(1,2,'d','e');
console.log(arr);
//参数1:从什么位置开始
//参数2:须要替换多少元素
//参数3:新的内容
复制代码

3.增

  • push
    • 能够在数组的最后新增一条数据, 而且会将新增内容以后数组当前的长度返回给咱们
    • 能够接收1个或多个参数
需求: 要求在数组最后添加一条数据
let res = arr.push('d');
console.log(res);  >>4
console.log(arr);  >>['a','b','c','d']
复制代码
  • unshift
    • 和push方法同样, 会将新增内容以后当前数组的长度返回给咱们
    • 能够接收1个或多个参数
需求: 要求在数组最前面添加一条数据
let res = arr.unshift('m');
console.log(res);  >>4
console.log(arr);  >>['a','b','c','m']
复制代码

4.删

  • pop
    • 能够删除数组中的最后一条数据, 而且将删除的数据返回给咱们
需求: 要求删除数组最后一条数据 
let res = arr.pop();
console.log(res);  >>c
console.log(arr);  >>['a','b']
复制代码
  • shift
    • 能够删除数组中的最前面一条数据, 而且将删除的数据返回给咱们
需求: 要求删除数组最前面一条数据
let res = arr.shift();
console.log(res);  >>a
console.log(arr);  >>['b','c']
复制代码
  • splice
    • 参数1: 从什么位置开始
    • 参数2: 须要删除多少个元素
需求: 要求删除数组中索引为1的数据
arr.splice(1,1);
console.log(arr);  >>['a','c']
复制代码
经常使用方法

1..如何清空数组

let arr = [1, 2, 3, 4, 5];
arr = [];  //第一种方法
arr.length = 0;  //第二种方法
arr.splice(0, arr.length)  //第三种方法
console.log(arr);  >>Array(0)
复制代码

2.如何将数组转换为字符串

let str = arr.toString();
console.log(str);  >>1,2,3,4,5
console.log(typeof str);  >>string
复制代码

3.如何将数组转换成指定格式字符串

  • join()
    • 默认状况下若是没有传递参数, 就是调用toString();
    • 若是传递了参数, 就会将传递的参数做为元素和元素的链接符号
let str = arr.join('+');
console.log(str);  >>1+2+3+4+5
console.log(typeof str);  >>string
复制代码

4.如何将两个数组拼接为一个数组

  • 数组不可以使用加号进行拼接, 若是使用加号进行拼接会先转换成字符串再拼接
  • concat()
    • 不会修改原有的数组, 会生成一个新的数组返回给咱们
  • 拓展运算符
    • 在解构赋值中(等号的左边)表示将剩余的数据打包成一个新的数组
    • 在等号右边, 那么表示将数组中全部的数据解开, 放到所在的位置
    • 不会修改原有的数组, 会生成一个新的数组返回给咱们
let arr1 = [1, 3, 5];
let arr2 = [2, 4, 6];
let res = arr1.concat(arr2);  //第一种方法
let res = [...arr1, ...arr2];  //第二种方法
console.log(res);  >>[1,3,5,2,4,6]
console.log(typeof res);  >>object
复制代码

5.如何对数组中的内容进行反转

  • reverse()
    • 会修改原有的数组
let res = arr.reverse();
console.log(res);  >>[5,4,3,2,1]
console.log(arr);  >>[5,4,3,2,1]
复制代码

6.如何截取数组中指定范围内容

  • slice()
    • 包头不包尾(包含起始位置, 不包含结束的位置)
let res = arr.slice(1,3);
console.log(res);  >>[2,3]
console.log(arr);  >>[1,2,3,4,5]
复制代码

7.如何查找元素在数组中的位置

  • indexOf()
    • 若是找到了指定的元素, 就会返回元素对应的位置
    • 若是没有找到指定的元素, 就会返回-1
    • 默认是从左至右的查找, 一旦找到就会当即中止查找
    • 参数1: 须要查找的元素
    • 参数2: 从什么位置开始查找
  • lastindexOf()
    • 默认是从右至左的查找, 一旦找到就会当即中止查找
let arr = [1, 2, 3, 4, 5, 3];
let res = arr.indexOf(3);   >>2
let res = arr.indexOf(3,4);   >>5
let res = arr.lastIndexOf(3,4);   >>2
console.log(res);
复制代码

8.如何判断数组中是否包含某个元素

  • includes()
    • 有就返回true,没有就返回false
//能够经过indexOf和lastIndexOf的结果, 判断是不是-1便可
let arr = [1, 2, 3, 4, 5];
let res = arr.indexOf(8);  >>-1
let res = arr.lastIndexOf(8);  >>-1
let res = arr.includes(4);  >>true
console.log(res);  
复制代码

9.练习

  • split()
    • 能够根据传入的参数切割字符串, 转换成一个数组以后返回给咱们
  • fill()
    • 设置数组中全部元素的值为指定的数据
//已知数组[3, 5, -2, 7, 4], 获取数组中的最大值并打印
let arr = [3,5,-2,7,4];
let max = arr[0];
for(let i=1;i<arr.length;i++){
	if(arr[1]>max){
		max=arr[i];
	}
}
console.log(max);

//要求用户输入3个0~9的数, 输出0~9中哪些用户没有输入过
let str = prompt('请输入三个整数,用逗号隔开');
let arr = str.split(',');

//第一种方法
for(let i=0;i<10;i++){
	if(arr[0] == i || arr[1] == i || arr[2] == i){
		continue;
	}
	console.log(i);
}

//第二种方法
let res = new Array(10);
for(let i=0;i<arr.length;i++){
	let res=arr[i];
	res[str]=666;
}
for(let i=0;i<res.length;i++){
	if(res[i]===666){
		continue;
	}
	console.log(i);
}

//从接盘接收5个0~9的数字, 排序后输出(数组计数)
let str = prompt("请输入五个整数, 用逗号隔开");
let arr = str.split(",");
let res = new Array(10);
res.fill(0);
for(let i=0;i<res.length;i++){
	let str=arr[i];
	res[str]=res[str]+1;
}
for(let j=0;j<res[i];j++){
	document.write(i);
	document.write('<br>');
}

//从接盘接收4个数字, 排序后输出
let str = prompt("请输入五个整数, 用逗号隔开");
let arr = str.split(",");
console.log(arr);

//第一种方法(数组选择)
for(let i=0;i<3;i++){
	for(let j=i;j<3;j++){
		if(arr[i]>arr[j+1]{
			let temp=arr[i];
			arr[i]=arr[j+1];
			arr[j+1]=temp;
		}
	}
	document.write('<br>');
}
//第二种方法(数组冒泡)
for(let i=0;i<3;i++){
	for(let j=0;j<3-i;j++){
		if(arr[j]>arr[j+1]){
			let temp=arr[j];
			arr[j]=arr[j+1];
			arr[j+1]=temp;
		}
	}
}
console.log(arr);
复制代码
二维数组

1.什么是二维数组?

  • 数组的每个元素又是一个数组, 咱们就称之为二维数组
let arr=[1,3,5];  //一维数组
let arr=[[1,3],[2,4]];  //二维数组
复制代码

2.如何操做二维数组?

  • 获取数据
    • 数组名称[二维数组索引];
    • 数组名称[二维数组索引] [一维数组索引];
let arr = [[1, 3], [2, 4]];
let arr1 = arr[0];  //获取一维
let ele = arr[0][1];  //获取二维
复制代码
  • 存储数据
    • 数组名称[二维数组索引] = 一维数组;
    • 数组名称[二维数组索引] [一维数组索引] = 值;
let arr = [[],[]];
arr[0] = [1, 3];
arr[1][0] = 2;
复制代码
  • 遍历数组
let arr = [[1, 3], [2, 4]];
for(let i=0;i<arr.length;i++){
	let subArray=arr[i];
	for(let j=0;j<subArray.length;j++){
		console.log(subArray[j]);
	}
}
复制代码

函数

1.什么是函数?

  • 函数是专门用于封装代码的, 函数是一段能够随时被反复执行的代码块

2.函数格式

function 函数名称(形参列表){
	被封装的代码;
}
复制代码

3.不使用函数的弊端

  • 冗余代码太多
  • 需求变动, 须要修改不少的代码

4.使用函数的好处

  • 冗余代码变少了
  • 需求变动, 须要修改的代码变少了
// 定义向左变道的函数
function toLeft(){
	console.log('打左转向灯');
	console.log("踩刹车");
	console.log("向左打方向盘");
	console.log("回正方向盘");
}
// 定义向右变道的函数
function toRight(){
	console.log("打右转向灯");
	console.log("向右打方向盘");
	console.log("回正方向盘");
}
// 向左变道
// 如下代码的含义: 找到名称叫作toLeft的函数, 执行这个函数中封装的代码
toLeft();
// 向右变道
toRight();
复制代码
定义步骤

1.书写函数的固定格式

2.给函数起一个有意义的名称

  • 为了提高代码的阅读性
  • 函数名称也是标识符的一种, 因此也须要遵照标识符的命名规则和规范3.

3.肯定函数的形参列表

  • 看看使用函数的时候是否须要传入一些辅助的数据

4.将须要封装的代码拷贝到{}中

5.肯定函数的返回值

  • 能够经过return 数据; 的格式, 将函数中的计算结果返回给函数的调用者
function getSum(a,b){
	let res=a+b;
	return res;
}
let num1=10;
let num2=20;
let result=getSum(num1,num2);
console.log(result);  >>30
复制代码

注意点

1.一个函数能够有形参也能够没有形参(零个或多个)

  • 什么是形参? 定义函数时函数()中的变量咱们就称之为形参
// 没有形参的函数
function say(){
	console.log('hello world');
}
say();
// 有形参的函数
function say(name){
	console.log('hello'+name);
}
say('lnj');
复制代码

2.一个函数能够有返回值也能够没有返回值

// 没有返回值的函数
function say(){
	console.log('hello world');
}
say();
// 有返回值的函数
function getSum(a,b){
	return a+b;
}
let res=getSum(10,20);
console.log(res);
复制代码

3.函数没有经过return明确返回值, 默认返回undefined

function say(){
	console.log("hello world");
	return;
}
let res = say();
console.log(res);  >>undefined
复制代码

4.return的做用和break类似, 因此return后面不能编写任何语句(永远执行不到)

  • break做用当即结束switch语句或者循环语句
  • return做用当即结束当前所在函数
function say(){
	console.log("hello world");
	return;
	console.log("return后面的代码");
}
say();  >>hello world
复制代码

5.调用函数时实参的个数和形参的个数能够不相同

  • 什么是实参? 调用函数时传入的数据咱们就称之为实参,实参能够是常量/变量

6.JavaScript中的函数和数组同样, 都是引用数据类型(对象类型)

  • 既然是一种数据类型, 因此也能够保存到一个变量中
  • 将一个函数保存到一个变量中,未来能够经过变量名称找到函数并执行函数
let say=function(){
	console.log("hello world");
}
say();
复制代码
arguments

1.由于console.log();也是经过()来调用的, 因此log也是一个函数

function say(){
	console.log('hello world');
}
window.say();
复制代码

2.log函数的特色

  • 能够接收1个或多个参数
console.log(1);
console.log(1,2);
console.log(1,2,3);
复制代码

3.为何log函数能够接收1个或多个参数

  • 内部的实现原理就用到了arguments

4.arguments的做用

  • 保存全部传递给函数的实参
//注意点: 每一个函数中都有一个叫作arguments的东东
// arguments实际上是一个伪数组
function getSum(){
	let sum=0;
	for(let i=0;i<arguments.length;i++){
		let num=arguments[i];
		sum +=num;
	}
	return sum;
}
let res=getSum(10,20,30);
console.log(res);  >>60
复制代码
扩展运算符(ES6)
  • 扩展运算符在等号左边, 将剩余的数据打包到一个新的数组中
    • 注意点: 只能写在最后
let [a, ...b]=[1,3,5];  >>a=1;b=[3,5];
复制代码
  • 扩展运算符在等号右边, 将数组中的数据解开
let arr1=[1,3,5];
let arr2=[2,4,6];
let arr=[...arr1, ...arr2]; 
>>let arr=[1,3,5,2,4,6];
复制代码
  • 扩展运算符在函数的形参列表中的做用
    • 将传递给函数的全部实参打包到一个数组中
    • 在等号左边同样, 也只能写在形参列表的最后
function getSum(...values){
	let sum=0;
	for(let i=0;i<values.length;i++){
		let num=values[i];
		sum +=num;
	}
	return sum;
}
let res=getSum(10,20,30);
console.log(res);  >>60
复制代码
形参默认值
  • 在ES6以前能够经过逻辑运算符来给形参指定默认值
格式: 条件A || 条件B
若是条件A成立, 那么就返回条件A
若是条件A不成立, 不管条件B是否成立, 都会返回条件B

function getSum(a,b){
	a = a || "指趣学院";
	b = b || "知播渔教育";
	console.log(a, b);
}
getSum(123, "abc");  >>指趣学院 知播渔教育
复制代码
  • 从ES6开始, 能够直接在形参后面经过=指定默认值
    • ES6开始的默认值还能够从其它的函数中获取
function getSum(a='指趣学院',b=getDefault()){
	console.log(a,b);
}
getSum();
function getDefault(){
	return '李南江'
}
复制代码
做为参数和返回值
  • 将函数做为其余函数的参数
let say=function(){
	console.log('hello world');
}
function test(fn){  //let fn=say;
	fn();
}
test(say);  >>hello world
复制代码
  • 将函数做为其余函数的返回值
    • 在其它编程语言中函数是不能够嵌套定义的,可是在JavaScript中函数能够
function test(){
	let say=function(){
		console.log('hello world');
	}
	return say;
}
let fn=test();  //let fn=say;
fn();  >>hello world
复制代码
匿名函数

1.什么是匿名函数?

  • 匿名函数就是没有名称的函数
// 有名称的函数
function say(){
	console.log('hello lnj');
}
// 匿名函数
function(){
	console.log('hello lnj');
}
复制代码

2.匿名函数的注意点

  • 匿名函数不可以只定义不使用

3.匿名函数的应用场景

  • 做为其余函数的参数
function test(fn){  //let fn=say;
	fn();
}
test(function(){
	console.log('hello world');
});
复制代码
  • 做为其余函数的返回值
function test(){
	return function(){
		console.log('hello world');
	};
}
let fn=test();
fn();
复制代码
  • 做为一个当即执行的函数
    • 若是想让匿名函数当即执行, 那么必须使用()将函数的定义包裹起来才能够
(function(){
	console.log('hello it666');
})();
复制代码
箭头函数(ES6)

1.什么是箭头函数?

  • 箭头函数是ES6中新增的一种定义函数的格式
  • 目的: 就是为了简化定义函数的代码

2.在ES6以前如何定义函数

function 函数名称(形参列表){
    须要封装的代码;
}
let 函数名称 = function(形参列表){
    须要封装的代码;
}
复制代码

3.从ES6开始如何定义函数

let 函数名称=(形参列表)=>{
	须要封装的代码;
}
复制代码

4.箭头函数的注意点

  • 在箭头函数中若是只有一个形参, 那么()能够省略
let say = name => {
    console.log("hello " + name);
}
say("it666");  >>hello it666
复制代码
  • 在箭头函数中若是{}中只有一句代码, 那么{}也能够省略
let say = name => console.log("hello " + name);
say("it666");  >>hello it666
复制代码
递归函数

1.什么是递归函数?

  • 就是在函数中本身调用本身,在必定程度上能够实现循环的功能

2.递归函数的注意点

  • 每次调用递归函数都会开辟一块新的存储空间, 因此性能不是很好
需求: 要求用户输入密码, 判断输入密码是否正确(假设正确密码是123456),若是正确, 输出"欢迎回来",若是不正确, 要求用户从新输入
function login(){
	let pwd=prompt('请输入密码');
	if(pwd != '123456'){
		login();
	}
	alert('欢迎回来');
}
login();
复制代码
变量做用域
  • 在JavaScript中{}外面的做用域, 咱们称之为全局做用域

  • 在JavaScript中函数后面{}中的的做用域, 咱们称之为"局部做用域"

  • 在ES6中只要{}没有和函数结合在一块儿, 那么应该"块级做用域"

{
	// 块级做用域
}

if(false){
	// 块级做用域
}

while(false){
	// 块级做用域
}

for(;;){
	// 块级做用域
}

do{
	// 块级做用域
}while(false);

switch(){
	// 块级做用域
}

function say(){
	// 局部做用域
}
复制代码
  • 块级做用域和局部做用域区别

    • 在块级做用域中经过var定义的变量是全局变量
    {
    	var num=123;  // 全局变量
    	let num=123;  // 局部变量
    }
    复制代码
    • 在局部做用域中经过var定义的变量是局部变量
    function test(){
    	var value=666;  // 局部变量
    	let value=666;  // 局部变量
    	value=666;  // 全局变量
    }
    复制代码
  • 不管是在块级做用域仍是在局部做用域, 省略变量前面的let或者var就会变成一个全局变量

var let
{}外 全局变量 全局变量
{}内 全局变量 局部变量
函数内 局部变量 局部变量
做用域链

注意点: 初学者在研究"做用域链"的时候最好将ES6以前和ES6分开研究

1.ES6以前

  • 须要明确:

    • 定义变量经过var
    • 没有块级做用域, 只有全局做用域和局部做用域
    • 函数大括号外的都是全局做用域,函数大括号中的都是局部做用域
  • 做用域链

    • 全局做用域咱们又称之为0级做用域
    • 定义函数开启的做用域就是1级/2级/3级/...做用域
    • JavaScript会将这些做用域连接在一块儿造成一个链条, 这个链条就是做用域链:0 ---> 1 ----> 2 ----> 3 ----> 4
    • 除0级做用域之外, 当前做用域级别等于上一级+1
  • 查找规则

    • 先在当前找, 找到就使用当前做用域找到的
    • 若是当前做用域中没有找到, 就去上一级做用域中查找
    • 以此类推直到0级为止, 若是0级做用域还没找到, 就报错
// 全局做用域 / 0级做用域
// var num = 123;
function demo(){
	// 1级做用域
	// var num = 456;
	function test(){
		// 2级做用域
		// var num = 789;
		console.log(num);
	}
	test();
}
demo();  >>789
复制代码

2.ES6

  • 须要明确
    • 定义变量经过let
    • 除了全局做用域、局部做用域之外, 还新增了块级做用域
    • 虽然新增了块级做用域, 可是经过let定义变量并没有差别(都是局部变量)
  • 做用域链
    • 全局做用域咱们又称之为0级做用域
    • 定义函数或者代码块都会开启的做用域就是1级/2级/3级/...做用域
    • JavaScript会将这些做用域连接在一块儿造成一个链条, 这个链条就是做用域链:0 ---> 1 ----> 2 ----> 3 ----> 4
    • 除0级做用域之外, 当前做用域级别等于上一级+1
  • 查找规则
    • 先在当前找, 找到就使用当前做用域找到的
    • 若是当前做用域中没有找到, 就去上一级做用域中查找
    • 以此类推直到0级为止, 若是0级做用域还没找到, 就报错
// 全局做用域 / 0级做用域
// let num = 123;
{
	// 1级做用域
	function test(){
		// 2级做用域
		// let num = 789;
		console.log(num);
	}
	test();
}
复制代码
预解析

1.什么是预解析?

  • 浏览器在执行JS代码的时候会分红两部分操做:预解析以及逐行执行代码
  • 也就是说浏览器不会直接执行代码, 而是加工处理以后再执行,这个加工处理的过程, 咱们就称之为预解析

2.预解析规则

  • 变量声明和函数声明提高到当前做用域最前面
  • 将剩余代码按照书写顺序依次放到后面

3.注意点

  • 经过let定义的变量不会被提高(不会被预解析)
// 预解析以前
console.log(num); //undefined
var num = 123;
// 预解析以后
var num;
console.log(num);
num = 123;

// 不会预解析以前
console.log(num); // 报错
let num = 456;

// ES6以前定义函数的格式

console.log(say);
say();
// ES6以前的这种定义函数的格式, 是会被预解析的, 因此能够提早调用
function say(){
	console.log("hello it666");
}
// 预解析以后的代码
function say(){
	console.log("hello it666");
}
say();

console.log(say);
say(); // say is not a function
// 若是将函数赋值给一个var定义的变量, 那么函数不会被预解析, 只有变量会被预解析
var say=function(){
	console.log("hello itzb");
}
var say; //undefined
say();
var say=function(){
	console.log("hello itzb");
}

// ES6定义函数的格式
say(); // say is not defined
let say=()=>{
	console.log("hello itzb");
}
复制代码
  • 在高级浏览器中, 不会对{}中定义的函数进行提高;只有在低级浏览器中, 才会按照正常的方式解析
//在ES6以前没有块级做用域, 而且没有将这两个函数定义到其它的函数中,因此这两个函数应该属于全局做用域
if(true){
	function demo(){
		console.log("hello demo1111111111");
	}
}else{
	function demo(){
		console.log("hello demo2222222222");
	}
}
demo();

function demo(){
	console.log("hello demo1111111111");
}
function demo(){
	console.log("hello demo2222222222");
}
if(true){}else{}
demo();
复制代码
  • 若是变量名称和函数名称同名, 那么函数的优先级高于变量
  • 必定要记住, 在企业开发中千万不要让变量名称和函数名称重名
console.log(value); // 会输出函数的定义
var value = 123;
function value(){
	console.log("fn value");
}
console.log(value); // 会输出123

function value(){
	console.log("fn value");
}
console.log(value);
var value;
value = 123;
console.log(value);
复制代码

面向对象

1.面向过程和面向对象的区别

  • 面向过程是解决这个问题须要哪些步骤
  • 面向对象是解决这个问题须要哪些对象
//买电脑
1.面向过程
了解电脑——了解本身的需求——对比参数——去电脑城——微信付钱——买回电脑——被坑
2.面向对象
找班长——描述需求——班长把电脑买回来
复制代码

2.JavaScript中提供了一个默认的类Object, 咱们能够经过这个类来建立对象

3.因为咱们是使用系统默认的类建立的对象, 因此系统不知道咱们想要什么属性和行为, 因此咱们必须手动的添加咱们想要的属性和行为

4.如何给一个对象添加属性

  • 对象名称.属性名称 = 值;

5.如何给一个对象添加行为

  • 对象名称.行为名称 = 函数;
// 建立对象的第一种方式
let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say=function(){
	console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();

// 建立对象的第二种方式
let obj = {}; // let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say=function(){
	console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();

// 建立对象的第三种方式
// 注意点: 属性名称和取值之间用冒号隔开, 属性和属性之间用逗号隔开
let obj={
	name: "lnj",
	age: 33,
	say:function(){
		console.log("hello world");
	}
}
console.log(obj.name);
console.log(obj.age);
obj.say();
复制代码
函数和方法的区别

1.什么是函数?

  • 函数就是没有和其它的类显示的绑定在一块儿的

2.什么是方法?

  • 方法就是显示的和其它的类绑定在一块儿的

3.函数和方法的区别

  • 函数能够直接调用, 可是方法不能直接调用, 只能经过对象来调用
  • 函数内部的this输出的是window, 方法内部的this输出的是当前调用的那个对象

4.不管是函数仍是方法, 内部都有一个叫作this的东东

  • this是什么? 谁调用了当前的函数或者方法, 那么当前的this就是谁
function demo(){
	console.log(this);
}
demo();  //window.demo();

let obj={
	name: "lnj",
	test:function(){
		console.log(this);
	}
}
test();  //obj.test();
复制代码
工厂函数

1.什么是工厂函数?

  • 就是专门用于建立对象的函数
function createPerson(myName,myAge){
	let obj = new Object();
	obj.name = myName;
	obj.age = myAge;
	obj.say=function(){
		console.log("hello world");
	}
	return obj;
}
let obj1 = createPerson("lnj", 34);
let obj2 = createPerson("zs", 44);
console.log(obj1);
console.log(obj2);
复制代码
构造函数

1.什么是构造函数

  • 和工厂函数同样, 都是专门用于建立对象的
  • 本质上是工厂函数的简写(更专业)

2.构造函数和工厂函数的区别

  • 构造函数的函数名称首字母必须大写
  • 构造函数只可以经过new来调用
function Person(myName,MyAge){
	// let obj = new Object(); // 系统自动添加的
	// let this = obj; // 系统自动添加的
	this.name = myName;
	this.age = myAge;
	this.say=function(){
		console.log("hello world");
	}
	// return this; // 系统自动添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1);
console.log(obj2);
//当咱们new Person("lnj", 34);系统作了什么事情
1)会在构造函数中自动建立一个对象
2)会自动将刚才建立的对象赋值给this
3)会在构造函数的最后自动添加return this;
复制代码
function Person(myName, myAge) {
    // let obj = new Object(); // 系统自动添加的
    // let this = obj; // 系统自动添加的
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        // 方法中的this谁调用就是谁, 因此当前是obj1调用, 因此当前的this就是obj1
        // console.log("hello world");
        console.log(this.name, this.age);
    }
    // return this; // 系统自动添加的
}
let obj1 = new Person("lnj", 34);
// console.log(obj1.name);
// console.log(obj1.age);
obj1.say();

function Person(myName, myAge) {
    // let obj = new Object(); // 系统自动添加的
    // let this = obj; // 系统自动添加的
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        console.log("hello world");
    }
    // return this; // 系统自动添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
// 因为两个对象中的say方法的实现都是同样的, 可是保存到了不一样的存储空间中,因此有性能问题
console.log(obj1.say === obj2.say); // false

function demo() {
    console.log("demo");
}
// 经过三个等号来判断两个函数名称, 表示判断两个函数是否都存储在同一块内存中
console.log(demo === demo); // true
复制代码

4.构造函数优化

  • 第一种:把函数放在全局变量中
function mySay(){
	console.log("hello world");
}
function Person(myName,Myage){
	// let obj = new Object(); // 系统自动添加的
	// let this = obj; // 系统自动添加的
	this.name = myName;
	this.age = myAge;
	this.say = mySay;
	// return this; // 系统自动添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1.say === obj2.say); // true

当前这种方式解决以后存在的弊端:
1)阅读性下降了
2)污染了全局的命名空间
复制代码
  • 第二种:函数做为方法放在对象中
let fns={
	test:function(){
		console.log("test");
	}
}
console.log(fns.test === fns.test); // true
// 因为test函数都是属于同一个对象, 因此返回true

let fns = {
    mySay: function () {
        console.log("hello world");
    }
}
function Person(myName, myAge) {
    // let obj = new Object(); // 系统自动添加的
    // let this = obj; // 系统自动添加的
    this.name = myName;
    this.age = myAge;
    this.say = fns.mySay;
    // return this; // 系统自动添加的
}
let obj1 = new Person("lnj", 34);
let obj2 = new Person("zs", 44);
console.log(obj1.say === obj2.say); // true
复制代码
  • 第三种:函数做为方法放在prototype(最重要、最专业)
function Person(myName, myAge) {
    // let obj = new Object(); // 系统自动添加的
    // let this = obj; // 系统自动添加的
    this.name = myName;
    this.age = myAge;
    // this.say = fns.mySay;
    // return this; // 系统自动添加的
}
Person.prototype = {
    say: function () {
        console.log("hello world");
    }
}
let obj1 = new Person("lnj", 34);
obj1.say();
let obj2 = new Person("zs", 44);
obj2.say();
console.log(obj1.say === obj2.say); // true
复制代码
prototype

1.特色

  • 存储在prototype中的方法能够被对应构造函数建立出来的全部对象共享
  • 除了能够存储方法之外, 还能够存储属性
  • 若是出现了和构造函数中同名的属性或者方法, 对象在访问的时候, 访问到的是构造函中的数据

2.应用场景

  • 通常状况下用于存储全部对象都相同的一些属性以及方法
  • 若是是对象特有的属性或者方法, 咱们会存储到构造函数中
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    this.currentType = "构造函数中的type";
    this.say = function () {
        console.log("构造函数中的say");
    }
}
Person.prototype = {
    currentType: "人",
    say: function () {
        console.log("hello world");
    }
}
let obj1 = new Person("lnj", 34);
obj1.say();  >>构造函数中的say
console.log(obj1.currentType);  >>构造函数中的type
let obj2 = new Person("zs", 44);
obj2.say();  >>构造函数中的say
console.log(obj2.currentType);  >>构造函数中的type
复制代码
对象三角恋关系
  • 每一个"构造函数"中都有一个默认的属性, 叫作prototype,prototype属性保存着一个对象, 这个对象咱们称之为"原型对象"
  • 每一个"原型对象"中都有一个默认的属性, 叫作constructor,constructor指向当前原型对象对应的那个"构造函数"
  • 经过构造函数建立出来的对象咱们称之为"实例对象",每一个"实例对象"中都有一个默认的属性, 叫作 proto,__proto__指向建立它的那个构造函数的"原型对象"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);

console.log(Person.prototype);  >>object
console.log(Person.prototype.constructor);  >>Person函数
console.log(obj1.__proto__);  >>object
复制代码
对象三角恋关系
Function函数
  • JavaScript中函数是引用类型(对象类型),既然是对象,因此也是经过构造函数建立出来的,"全部函数"都是经过Function构造函数建立出来的对象
  • JavaScript中只要是"函数"就有prototype属性,"Function函数"的prototype属性指向"Function原型对象"
  • JavaScript中只要"原型对象"就有constructor属性,"Function原型对象"的constructor指向它对应的构造函数
  • Person构造函数是Function构造函数的实例对象, 因此也有__proto__属性,Person构造函数的__proto__属性指向"Function原型对象"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);
console.log(Function === Function.prototype.constructor); // true
console.log(Person.__proto__ === Function.prototype); // true
复制代码
Object函数
  • JavaScript函数是引用类型(对象类型), 因此Function函数也是对象
  • "Function构造函数"也是一个对象, 因此也有__proto__属性, "Function构造函数"__proto__属性指向"Function原型对象"
  • JavaScript中还有一个系统提供的构造函数叫作Object,只要是函数都是"Function构造函数"的实例对象
  • 只要是对象就有__proto__属性, 因此"Object构造函数"也有__proto__属性,"Object构造函数"的__proto__属性指向建立它那个构造函数的"原型对象"
  • 只要是构造函数都有一个默认的属性, 叫作prototype,prototype属性保存着一个对象, 这个对象咱们称之为"原型对象"
  • 只要是原型对象都有一个默认的属性, 叫作constructor,constructor指向当前原型对象对应的那个"构造函数"
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
let obj1 = new Person("lnj", 34);
console.log(Function.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Object.prototype.constructor === Object); // true
console.log(Object.prototype.__proto__); // null
复制代码
函数对象关系
  • Function函数是全部函数的祖先函数
  • 全部的构造函数都有一个prototype属性, 全部prototype属性都指向本身的原型对象
  • 全部的原型对象都有一个constructor属性, 全部constructor属性都指向本身的构造函数
  • 全部函数都是Function构造函数的实例对象
  • 全部函数都是对象, 包括Function构造函数
  • 全部对象都有__proto__属性
  • 普通对象的__proto__属性指向建立它的那个构造函数对应的"原型对象"
  • 全部对象的__proto__属性最终都会指向"Object原型对象"
  • "Object原型对象"的__proto__属性指向NULL

2

原型链
  • 对象中__proto__组成的链条咱们称之为原型链,如:实例对象——Person原型对象——Object原型对象
  • 对象在查找属性和方法的时候, 会先在当前对象查找,若是当前对象中找不到想要的, 会依次去上一级原型对象中查找,若是找到Object原型对象都没有找到, 就会报错
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    // this.currentType = "构造函数中的type";
    // this.say = function () {
    // console.log("构造函数中的say");
    // }
}
Person.prototype = {
    // 注意点: 为了避免破坏原有的关系, 在给prototype赋值的时候, 须要在自定义的对象中手动的添加constructor属性, 手动的指定须要指向谁
    constructor: Person,
    // currentType: "人",
    // say: function () {
    // console.log("hello world");
    // }
}
let obj1 = new Person("lnj", 34);
// obj1.say(); 
console.log(obj1.currentType);  >>报错
// console.log(Person.prototype.constructor);
复制代码
属性注意点
  • 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 若是当前对象没有就会给当前对象新增一个不存在的属性
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
Person.prototype = {
    constructor: Person,
    currentType: "人",
    say: function () {
        console.log("hello world");
    }
}
let obj = new Person("lnj", 34);
obj.currentType = "新设置的值";
console.log(obj.currentType); // 新设置的值
console.log(obj.__proto__.currentType); // "人"
复制代码
  • 因为私有属性的本质就是一个局部变量, 并非真正的属性, 因此若是经过 对象.xxx的方式是找不到私有属性的, 因此会给当前对象新增一个不存在的属性
function Person() {
    this.name = "lnj";
    let age = 34;
    this.setAge = function (myAge) {
        if (myAge >= 0) {
            age = myAge;
        }
    }
    this.getAge = function () {
        return age;
    }
    this.say = function () {
        console.log("hello world");
    }
}
let obj = new Person();
// 1.操做的是私有属性(局部变量)
obj.setAge(-3);
console.log(obj.getAge());
// 2.操做的是公有属性
obj.age = -3;
console.log(obj.age);
复制代码
属性方法分类
  • 实例属性/实例方法
    • 在企业开发中经过实例对象访问的属性, 咱们就称之为实例属性
    • 在企业开发中经过实例对象调用的方法, 咱们就称之为实例方法
  • 静态属性/静态方法
    • 在企业开发中经过构造函数访问的属性, 咱们就称之为静态属性
    • 在企业开发中经过构造函数调用的方法, 咱们就称之为静态方法
function Person() {
    this.name = "lnj";
    this.say = function () {
        console.log("hello world");
    }
}
     
// 经过构造函数建立的对象, 咱们称之为"实例对象"
let obj = new Person();
console.log(obj.name);
obj.say();

obj.age = 34;
console.log(obj.age);
obj.eat = function () {
    console.log("eat");
}
obj.eat();
// 构造函数也是一个"对象", 因此咱们也能够给构造函数动态添加属性和方法
Person.num = 666;
Person.run = function () {
    console.log("run");
}
console.log(Person.num);
Person.run();
复制代码
bind-call-apply

1.this是什么?

  • 谁调用当前函数或者方法, this就是谁

2.这三个方法的做用是什么?

  • 用于修改函数或者方法中的this的

3.三者区别

  • bind方法做用
    • 修改函数或者方法中的this为指定的对象, 而且会返回一个修改以后的新函数给咱们
    • bind方法除了能够修改this之外, 还能够传递参数, 只不过参数必须写在this对象的后面
  • call方法做用
    • 修改函数或者方法中的this为指定的对象, 而且会当即调用修改以后的函数
    • call方法除了能够修改this之外, 还能够传递参数, 只不过参数必须写在this对象的后面
  • apply方法做用
    • 修改函数或者方法中的this为指定的对象, 而且会当即调用修改以后的函数
    • apply方法除了能够修改this之外, 还能够传递参数, 只不过参数必须经过数组的方式传递
let obj={
	name:'zs';
}
function test(a,b){
	console.log(a,b);
	console.log(this);
}
//修改属性
let fn=test.bind(obj,10,20);
fn();  >>10 20 {name:'zs'}

test.call(obj,10,20);  >>10 20 {name:'zs'}

test.apply(obj,[10,20]);  >>10 20 {name:'zs'}

//修改方法
function Person(){
    this.name='lnj';
    this.say=function(){
        console.log(this);
    }
}
let p=new Person();

let fn=p.say.bind(obj);
fn();  >>{name:'zs'}

p.say.call(obj);  >>{name:'zs'}

p.say.apply(obj);  >>{name:'zs'}
复制代码
面向对象三大特性

1.封装性

  • 局部变量和局部函数
    • 不管是ES6以前仍是ES6, 只要定义一个函数就会开启一个新的做用域
    • 只要在这个新的做用域中, 经过let/var定义的变量就是局部变量
    • 只要在这个新的做用域中, 定义的函数就是局部函数
  • 什么是对象的私有变量和函数
    • 默认状况下对象中的属性和方法都是公有的, 只要拿到对象就能操做对象的属性和方法
    • 外界不能直接访问的变量和函数就是私有变量和私有函数
    • 构造函数的本质也是一个函数, 因此也会开启一个新的做用域, 因此在构造函数中定义的变量和函数就是私有函数
  • 什么是封装?
    • 封装性就是隐藏实现细节,仅对外公开接口
  • 为何要封装?
    • 不封装的缺点:当一个类把本身的成员变量暴露给外部的时候,那么该类就失去对属性的管理权,别人能够任意的修改你的属性
    • 封装就是将数据隐藏起来,只能用此类的方法才能够读取或者设置数据,不可被外部任意修改. 封装是面向对象设计本质(将变化隔离)。这样下降了数据被误用的可能 (提升安全性和灵活性)
function Person() {
    this.name = "lnj";
    // this.age = 34;
    let age = 34;
    this.setAge = function (myAge) {
        if(myAge >= 0){
            age = myAge;
        }
    }
    this.getAge = function () {
        return age;
    }
    this.say = function () {
        console.log("hello world");
    }
    // 因为构造函数也是一个函数, 因此也会开启一个新的做用域
    // 因此在构造函数中经过var/let定义的变量也是局部变量
    // 因此在构造函数中定义的函数也是局部函数
    var num = 123;
    let value = 456;
    function test() {
        console.log("test");
    }
}
let obj = new Person();
// 结论: 默认状况下对象的属性和方法都是公开的, 只要拿到对象就能够操做对象的属性和方法
// console.log(obj.name);
// obj.age = -3;
// console.log(obj.age);
// obj.say();
复制代码

2.继承性

  • 简介

    • 儿子继承父亲的物品就是继承最好的体现
    • 目的:把子类型中共同的属性和方法提取到父类型中
    • 优点:较少代码的冗余度,提高代码的复用性
  • 继承方式一

    • 在企业开发中若是构造函数和构造函数之间的关系是is a关系, 那么就可使用继承来优化代码, 来减小代码的冗余度
    • 学生 is a 人 , 学生是一我的
    • 弊端:后构造函数没法同时使用前构造函数和新增的形参
function Person() {
    this.name = null;
    this.age = 0;
    this.say = function () {
        console.log(this.name, this.age);
    }
}
let per = new Person();
per.name = "lnj";
per.age = 34;
per.say();

function Student() {
    // this.name = null;
    // this.age = 0;
    // this.say = function () {
    // console.log(this.name, this.age);
    // }
    this.score = 0;
    this.study = function () {
        console.log("day day up");
    }
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

//弊端
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        console.log(this.name, this.age);
    }
}
function Student(myName, myAge, myScore) {
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
复制代码
  • 继承方式二(call)
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    this.say = function () { // stu.say = function () {}
        console.log(this.name, this.age);
    }
    // return this;
}
function Student(myName,myAge,myScore){
    //let stu=new Object();
    //let this=stu;
    Person.call(this,myName,myAge);
    this.score=myScore;
    this.study=function(){
        console.log('day day up');
    }
    return this;
}
let stu=new Student('ww',19,99);
console.log(stu.score);  >>99
stu.say();  >>19,99
stu.study();  >>day day up
复制代码
  • 继承方式三(把父构造函数的原型对象里的东西用到子构造函数)
    • 弊端
      • 因为修改了Person原型对象的constructor属性, 因此破坏了Person的三角恋关系
      • 因为Person和Student的原型对象是同一个, 因此给Student的元素添加方法, Person也会新增方法
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    // return this;
}
Person.prototype.say = function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
// 注意点: 要想使用Person原型对象中的属性和方法, 那么就必须将Student的原型对象改成Person的原型对象才能够
Student.prototype=Person.prototype;
Student.prototype.constructor=Student;
let stu = new Student("ww", 19, 99);
console.log(stu.score);  >>99
stu.say();  >>ww,19
stu.study();  >>day day up
复制代码
  • 继承方式四(终极方法,记住这个就能够了,最重要)
    • 在子类的构造函数中经过call借助父类的构造函数
    • 将子类的原型对象修改成父类的实例对象
function Person(myName, myAge) {
    // let per = new Object();
    // let this = per;
    // this = stu;
    this.name = myName; // stu.name = myName;
    this.age = myAge; // stu.age = myAge;
    // return this;
}
Person.prototype.say = function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
Student.prototype=new Person();
Student.prototype.constructor=Student;
Student.prototype.run=function(){
    console.log('run');
}
let per=new Person();
per.run();  >>报错
复制代码

3.多态性

  • 什么是强类型语言, 什么是是弱类型语言

    • 强类型语言
      • 通常编译型语言都是强类型语言,要求变量的使用要严格符合定义
      • 例如定义 int num; 那么num中未来就只可以存储整型数据
    • 弱类型语言
      • 通常解释型语言都是弱类型语言,不会要求变量的使用要严格符合定义
      • 例如定义 let num; num中既能够存储整型, 也能够存储布尔类型等
      • 因为js语言是弱类型的语言, 因此咱们不用关注多态
  • 什么是多态?

    • 指事物的多种状态
    • 例如:按下 F1 键这个动做,若是当前在 webstorm 界面下弹出的就是 webstorm 的帮助文档;若是当前在 Word 下弹出的就是 Word 帮助;同一个事件发生在不一样的对象上会产生不一样的结果。
  • 多态在编程语言中的体现

    • 父类型变量保存子类型对象, 父类型变量当前保存的对象不一样, 产生的结果也不一样
//强类型语言(继承+多态)
function Animal(myName) {
    this.name = myName;
    this.eat = function () {
        console.log(this.name + " 动物吃东西");
    }
}
function Dog() {
    Animal.call(this, myName);
    this.eat = function () {
        console.log(" 狗吃东西");
    }
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
function Cat() {
    Animal.call(this, myName);
    this.eat = function () {
        console.log(" 猫吃东西");
    }
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
function feed(Animal animal) {
    animal.eat(); // 狗吃东西
}
//弱类型语言
function Dog() {
    //Animal.call(this, myName);
    this.eat = function () {
        console.log(" 狗吃东西");
    }
}
function Cat() {
    //Animal.call(this, myName);
    this.eat = function () {
        console.log(" 猫吃东西");
    }
}
function feed(animal){
    animal.eat();
}
let dog = new Dog();
feed(dog);  >>狗吃东西
let cat = new Cat();
feed(cat);  >>猫吃东西
复制代码
ES6类和对象

1.在ES6以前若是定义一个类?

  • 经过构造函数来定义一个类
function Person(myName, myAge) {
    // 实例属性
    // this.name = "lnj";
    // this.age = 34;
    this.name = myName;
    this.age = myAge;

    // 实例方法
    this.say = function () {
        console.log(this.name, this.age);
    }
    // 静态属性
    Person.num = 666;
            // 静态方法
    Person.run = function () {
        console.log("run");
    }
}
// let p = new Person();
let p = new Person("zs", 18);
p.say();
console.log(Person.num);
Person.run();
复制代码
  • 从ES6开始系统提供了一个名称叫作class的关键字, 这个关键字就是专门用于定义类的
    • 当咱们经过new建立对象的时候, 系统会自动调用constructor,constructor咱们称之为构造函数
    • 在ES6标准中添加实例属性都须要在constructor中添加
    • 在ES标准中static只支持定义静态方法不支持定义静态变量
class Person{
    //实例属性
    constructor(myName,myAge){
        this.name=myName;
        this.age=myAge;
        this.hi=function(){
            console.log('hi');
        }
    }
    // 如下定义"实例属性"的方式并非ES6正式版标准中的写法, 大部分的浏览器不支持
    // name = "lnj";
    // age = 34;
    //实例方法(会自动添加到原型位置中,若是想要该方法放在原函数里,那就放在constructor里)
	say(){
        console.log(this.name,this.age);
    }
	//静态属性
    // 如下定义"静态属性"的方式并非ES6正式版标准中的写法, 大部分的浏览器不支持
    static num=666;
	//静态方法
	static run(){
        console.log('run');
    }
}
let p=new Person('zs',18);
p.say();  >>zs,18
console.log(Person.rum);  >>666
Person.run();  >>run
复制代码
  • 在原型上添加方法和属性
    • 若是经过class定义类, 那么不能自定义这个类的原型对象
    • 若是想将属性和方法保存到原型中, 只能动态给原型对象添加属性和方法
//构造函数
//第一种方法:
Person.prototype.type='人';
Person.prototype.say=function(){
    console.log(this.name,this.age);
};
//第二种方法:
Person.prototype={
    constructor:Person,
    type:'人',
    say:function(){
        console.log(this.name,this.age);
    }
};
//ES6类
Person.prototype.type='人';
Person.prototype.say=function(){
    console.log(this.name,this.age);
};
复制代码
ES6继承
  • ES6以前的继承
    • 在子类中经过call/apply方法借助父类的构造函数
    • 将子类的原型对象设置为父类的实例对象
function Person(myName, myAge) {
    this.name = myName;
    this.age = myAge;
}
Person.prototype.say =  function () {
    console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
    // 1.在子类中经过call/apply方法借助父类的构造函数
    Person.call(this, myName, myAge);
    this.score = myScore;
    this.study = function () {
        console.log("day day up");
    }
}
// 2.将子类的原型对象设置为父类的实例对象
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student("zs", 18, 99);
stu.say();
复制代码
  • ES6开始的继承
    • 在子类后面添加extends并指定父类的名称
    • 在子类的constructor构造函数中经过super方法借助父类的构造函数
class Person{
    constructor(myName, myAge){
        // this = stu;
        this.name = myName; // stu.name = myName;
        this.age = myAge; // stu.age = myAge;
    }
    say(){
        console.log(this.name, this.age);
    }
}
// 如下代码的含义: 告诉浏览器未来Student这个类须要继承于Person这个类
class Student extends Person{
    constructor(myName,myAge,myScore){
        super(myName,myAge);  //至关于Person.call(this,myName,myAge);
        this.score=myScore;
    }
    study(){
        console.log('day day up');
    }
}
let stu=new Student('zs',18,98);
stu.say();
复制代码
获取对象类型
let obj = new Object();
console.log(typeof obj); // object

let arr = new Array();
console.log(arr.constructor.name);  //Array

function Person() {
    // let obj = new Object();
    // let this = obj;
    this.name = "lnj";
    this.age = 34;
    this.say = function () {
        console.log(this.name, this.age);
    }
    // return this;
}
let p = new Person();
// console.log(typeof p); // object
console.log(p.constructor.name); // Person
复制代码
instanceof关键字

1.什么是instanceof关键字?

  • instanceof用于判断 "对象" 是不是指定构造函数的 "实例"

2.instanceof注意点

  • 只要构造函数的原型对象出如今实例对象的原型链中都会返回true
class Person{
    name = "lnj";
}
let p = new Person();
console.log(p instanceof Person);  >>true

class Cat{
    name = "mm";
}
let c = new Cat();
console.log(c instanceof Person); // false

function Person(myName) {
    this.name = myName;
}
function Student(myName, myScore) {
    Person.call(this, myName);
    this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student();
console.log(stu instanceof Person); // true
复制代码
isPrototypeOf属性

1.什么是isPrototypeOf属性

  • 用于判断 一个对象是不是另外一个对象的原型

2.isPrototypeOf注意点

  • 只要调用者在传入对象的原型链上都会返回true
class Person{
    name = "lnj";
}
let  p = new Person();
console.log(Person.prototype.isPrototypeOf(p)); // true

class Cat{
    name = "mm";
}
console.log(Cat.prototype.isPrototypeOf(p)); // false

function Person(myName) {
    this.name = myName;
}
function Student(myName, myScore) {
    Person.call(this, myName);
    this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student();
console.log(Person.prototype.isPrototypeOf(stu)); // true
复制代码
判断对象属性
  • in
  • hasOwnProperty
// 需求: 判断某一个对象是否拥有某一个属性
class Person{
    name=null;
	age=0;
}
Person.prototype.height=0;
let p = new Person();
// in的特色: 只要类中或者原型对象中有, 就会返回true
console.log("name" in p); // true
console.log("height" in p); // true

// 需求: 判断某一个对象自身是否拥有某一个属性
// hasOwnProperty特色: 只会去类中查找有没有, 不会去原型对象中查找
console.log(p.hasOwnProperty("name")); // true
console.log(p.hasOwnProperty("height")); // false
复制代码
对象增删改查

1.增长(C)

class Person{}
let p=new Person();
//增长属性
p.name='gh';
p['name']='hj';
//增长方法
p.say=function(){
   console.log('hello');
}
p['say']=function(){
   console.log('hello');
}
复制代码

2.删除(R)

//删除属性
delete p.name;
delete p['name'];

//删除方法
delete p.say;
delete p['say'];
复制代码

3.修改(U)

p.name = "lnj";
p["name"] = "ww";
console.log(p.name);

p.say = function(){
    console.log("hi");
}
p["say"] = function(){
    console.log("hi");
}
p.say();
复制代码

4.查询(D)

console.log(p.name);
console.log(p["name"]);
p.say();
复制代码
对象遍历

1.在JavaScript中对象和数组同样是能够遍历的

2.什么是对象的遍历?

  • 对象的遍历就是依次取出对象中全部的属性和方法

3.如何遍历一个对象?

  • 在JS中能够经过高级for循环来遍历对象
//如下代码的含义: 将指定对象中全部的属性和方法的名称取出来了依次的赋值给key这个变量
for(let key in obj){}

// ES6
class Person{
    constructor(myName, myAge){
        this.name = myName;
        this.age = myAge;
    }
    // 注意点: ES6定义类的格式, 会将方法默认放到原型对象中,遍历取不出来
    say(){
        console.log(this.name, this.age);
    }
}
let p=new Person("LNJ", 34);
for(let key in p){
    console.log(key);
}
>>lnj 34
复制代码
  • ES6以前
function Person(myName, myAge){
    this.name = myName;
    this.age = myAge;
    this.say = function(){
        console.log(this.name, this.age);
    }
}
let p = new Person("LNJ", 34);
for(let key in p){
    if(p[key] instanceof Function){
        continue;  //判断是否是函数
    }
    // console.log(key); // name / age / say
    // 注意点: 如下代码的含义取出p对象中名称叫作当前遍历到的名称的属性或者方法的取值
    console.log(p[key]); // p["name"] / p["age"] / p["say"]
    // 注意点: 如下代码的含义取出p对象中名称叫作key的属性的取值
    // console.log(p.key); // undefined
}
复制代码
对象解构赋值

1.注意点

  • 对象的解构赋值和数组的解构赋值 除了符号不同, 其它的如出一辙
  • 数组解构使用[]
  • 对象解构使用{}

2.在数组的解构赋值中, 等号左边的格式必须和等号右边的格式如出一辙, 才能彻底解构

let [a, b, c] = [1, 3, 5];
console.log(a, b, c); // 1 3 5
复制代码

3.在数组的解构赋值中, 两边的个数能够不同

let [a, b] = [1, 3, 5];
console.log(a, b); // 1 3
let [a, b, c] = [1, 3];
console.log(a, b, c); // 1 3 undefined

复制代码

4.在数组的解构赋值中,若是右边少于左边, 咱们能够给左边指定默认值

let [a, b, c = 666] = [1, 3];
console.log(a, b, c); // 1 3 666
复制代码

5.练习

let obj={
    name:'lk',
    age:35
}
let {name,age}=obj;
let {name,age}={name:'lk',age:35};
console.log(name,age);  >>lk,35

let {name}={name:'lk',age:35};
console.log(name,age);  >>lk

let {name, age, height} = {name: "lnj",age: 34};
console.log(name, age, height);  >>lnj,34,undefined

let {name, age, height = 1.80} = {name: "lnj",age: 34};
console.log(name, age, height);  >>lnj,34,1.80

//注意点: 在对象解构赋值中, 左边的变量名称必须和对象的属性名称一致, 才能解构出数据
let {age} = {name: "lnj",age: 34};
console.log(age); // 34
let {a, b} = {name: "lnj",age: 34};
console.log(a, b); // undefined undefined
复制代码

6.应用场景

//数组应用
let arr=[1,3];
function sum([a,b]){
    return a+b;
}
let res=sum(arr);
console.log(res);  >>4
//对象应用
let obj={
    name:'lk',
    age:15
}
function say({name,age}){
    console.log(name,age);
}
say(obj);  >>lk,15
复制代码
深拷贝和浅拷贝

1.什么是深拷贝什么是浅拷贝?

  • 深拷贝
    • 修改新变量的值不会影响原有变量的值
    • 默认状况下基本数据类型都是深拷贝
  • 浅拷贝
    • 修改新变量的值会影响原有的变量的值
    • 默认状况下引用类型都是浅拷贝
//深拷贝
let num1 = 123;
let num2 = num1;
num2 = 666; // 修改形变量的值
console.log(num1);
console.log(num2);
//浅拷贝
class Person{
    name = "lnj";
    age = 34;
}
let p1 = new Person();
let p2 = p1;  //传递内存地址
p2.name = "zs"; // 修改变量的值
console.log(p1.name);  >>zs
console.log(p2.name);  >>zs
复制代码

2.对象深拷贝

  • assign() —— 只对属性有效
class Person{
    name = "lnj";
    age = 34;
}
let p1 = new Person();
let p2 = new Object();
//第一种方式
p2.name = p1.name;
p2.age = p1.age;
p2.name = "zs";
//第二种方式
for(let key in p1){
    p2[key]=p1[key];
}
p2.name='zs';
//第三种方式
// assign方法能够将第二个参数的对象的属性和方法拷贝到第一个参数的对象中
Object.assign(p2, p1);
p2.name = "zs";
console.log(p1.name);
console.log(p2.name);
// 注意点: 只有被拷贝对象中全部属性都是基本数据类型, 以上代码才是深拷贝
复制代码
  • depCopy()
class Person{
    name = "lnj";
    cat = {
        age : 3
    };
    scores = [1, 3, 5];
}
let p1 = new Person();  >>3
let p2 = new Object();  >>666
depCopy(p2,p1);
function depCopy(target,source){
    // 1.经过遍历拿到source中全部的属性
    for(let key in source){
        // 2.取出当前遍历到的属性对应的取值
        let sourceValue=source[key];
    	// 3.判断当前的取值是不是引用数据类型
        if(sourceValue instanceof Object){
            let subTarget new sourceValue.constructor;
            target[key]=subTarget;
            depCopy(subTarget,sourceValue);
        }else{
            target[key]=sourceValue;
        }
    }
}
复制代码

3.调试技巧

F12—Source:能够断点看函数的执行过程

数组高级API

1.遍历数组

  • for in
    • 注意点: 在企业开发中不推荐使用forin循环来遍历数组

MDN的解释 developer.mozilla.org/zh-CN/docs/…

  • for of
  • forEach
    • forEach方法会自动调用传入的函数
    • 每次调用都会将当前遍历到的元素和当前遍历到的索引和当前被遍历的数组传递给这个函数
// 需求: 要求遍历数组
// 1.利用传统循环来遍历数组
let arr = [1, 3, 5, 7, 9];
for(let i=0;i<arr.length;i++){
    console.log(arr[i]);
}
//2.利用forin循环来遍历数组
for(let key in arr){
    console.log(arr[key]);
}

function Person() {
    this.name = "lnj";
    this.age = 34;
    this.score = 99;
}
// 注意点: 对象中的属性是无序的
// forin循环是专门用于遍历对象的, 可是对象的属性是无序的, 因此forin循环就是专门用于遍历无序的东西的, 因此不推荐使用forin循环来遍历数组
let p = new Person();
console.log(p);

//3.利用ES6中推出的for of循环来遍历数组
for(let value of arr){
    console.log(value);
}

//4.还能够利用Array对象的forEach方法来遍历数组
arr.forEach(function(currentValue,currentIndex,curentArray){
    console.log(currentValue);
});
//forEach实现原理
Array.prototype.myForEach=function(fn){
    for(let i=0;i<this.length;i++){
        fn(this[i],i,this);
    }
};
arr.myForEach(function(currentValue,currentIndex,currentArray){
    console.log(currentValue,currentIndex,currentArray);
});
复制代码

2.数组查找

  • findIndex方法
    • 定制版的indexOf, 找到返回索引, 找不到返回-1
  • find方法
    • find方法返回找到的元素
    • find方法若是找到了就返回找到的元素, 若是找不到就返回undefined
let arr = [3, 2, 6, 7, 6];
// 从左往右查找, 找到返回索引, 找不到返回-1
let index1 = arr.indexOf(6);
console.log(index1); // 2
// 从右至左查找, 找到返回索引, 找不到返回-1
let index2 = arr.lastIndexOf(6);
console.log(index2); // 4
// 从左往右查找, 找到返回true, 找不到返回false
let result = arr.includes(6);
console.log(result); // true

//数组的findIndex方法
let index = arr.findIndex(function (currentValue, currentIndex, currentArray) {
    // console.log(currentValue, currentIndex, currentArray);
    // if(currentValue === 6){ 
    if(currentValue === 10){
        return true;
    }
});
console.log(index);  >>-1
// findIndex实现
Array.prototype.myFindIndex = function (fn) {
    for(let i = 0; i < this.length; i++){
        let result = fn(this[i], i, this);
        if(result){
            return i;
        }
    }
    return -1;
}

//数组的find方法
let value = arr.find(function (currentValue, currentIndex, currentArray) {
    // console.log(currentValue, currentIndex, currentArray);
    // if(currentValue === 6){
    if(currentValue === 10){
        return true;
    }
});
console.log(value);  >>undefined
// findIndex实现
Array.prototype.myFind = function (fn) {
    for(let i = 0; i < this.length; i++){
        let result = fn(this[i], i, this);
        if(result !== undefined){
            return result;
        }
    }
    return undefined;
}
复制代码

3.数组过滤和映射

  • filter方法
    • 将知足条件的元素添加到一个新的数组中
let arr = [1, 2, 3, 4, 5];
let newArray=arr.filter(function(currentValue,currentIndex,currentArray){
    if(currentValue % 2 === 0){
        return true;
    }
});
console.log(newArray);  >>[2,4]
// filter实现
Array.prototype.myFilter=function(fn){
    let newArray=[];
    for(let i=0;i<this.length;i++){
        let result=fn(this[i],i,this);
        if(result){
            newArray.push(this[i]);
        }
    }
    return newArray;
}
复制代码
  • map方法
    • 将知足条件的元素映射到一个新的数组中
let newArray=arr.map(function(currentValue,currentIndex,currentArray){
    if(currentValue % 2 === 0){
        return currentValue;
    }
});
console.log(newArray);  >>[undefined,2,undefined,4,undefined]
// map实现
Array.prototype.myMap=function(fn){
    let newArray=new Array(this.length);
    newArray.fill(undefined);
    for(let i=0;i<this.length;i++){
        let result=fn(this[i],i,this);
        if(result !== undefined){
            newArray[i]=result;
        }
    }
    return newArray;
}
复制代码
删除数组元素注意点
  • splice
  • delete
let arr = [1, 2, 3, 4, 5];
// 需求: 遍历的同时删除数组中全部元素
//第一种方法
let len=arr.length;
for(let i=len-1;i>=0;i--){
    arr.splice(i,1);
}
//第二种方法
// 注意点: 经过delete来删除数组中的元素, 数组的length属性不会发生变化
for(let i=0;i<arr.length;i++){
    console.log(arr.length);  >>5
    delete arr[i];
}
console.log(arr);  >>[1, 2, 3, 4, 5]

复制代码
数组排序方法
  • compareFunction(a, b)
    • 若是 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 以前;
    • 若是 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。
    • 若是 compareFunction(a, b) 大于 0 , b 会被排列到 a 以前
    • 注意点: 若是元素是字符串类型, 那么比较的是字符串的Unicode编码
let arr = ["c", "a", "b"];
//arr.sort();
arr.sort(function(a,b){
    if(a>b){
        return -1;
    }else if(a<b){
        return 1;
    }else{
        return 0;
    }
});
console.log(arr);

//规律: 若是数组中的元素是数值类型,若是须要升序排序, 那么就返回a - b;若是须要降序排序, 那么就返回b - a;
let arr = [3, 4, 2, 5, 1];
// arr.sort();
arr.sort(function(a,b){
	return a-b;
});
console.log(arr);

let arr = ["1234", "21", "54321", "123", "6"];
arr.sort(function(str1,str2){
    return str2.length-str1.length;
});
console.log(arr);

let students = [
    {name: "zs", age: 34},
    {name: "ls", age: 18},
    {name: "ww", age: 22},
    {name: "mm", age: 28},
];
students.sort(function (o1, o2) {
    // return o1.age - o2.age;
    return o2.age - o1.age;
});
console.log(students);
复制代码
字符串经常使用方法
  • 在js中字符串能够看作一个特殊的数组, 因此大部分数组的属性/方法字符串均可以使用
// 1.获取字符串长度 .length
let str='abcd';
console.log(str.length);  >>4

// 2.获取某个字符 [索引] / charAt
let str='abcd';
//第一种方法,仅高级浏览器支持
let ch=str[1];  
//第二种方法,没有兼容性问题
let ch=str.charAt(1);  
console.log(ch);  >>a

// 3.字符串查找 indexOf / lastIndexOf / includes
let str = "vavcd";
let index=str.indexOf('v');  >>0
let index=str.lastIndexOf('v');  >>2
console.log(index);
let result=str.includes('p');
console.log(result);  >>false

// 4.拼接字符串 concat / +
let str1 = "www";
let str2 = "it666";
let str=str1+str2;  //推荐
let str=str1.concat(str2);
console.log(str);

// 5.截取子串 slice / substring / substr
let str = "abcdef";
let subStr=str.slice(1,3);  >>bc
let subStr=str.substring(1,3);  >>bc
let subStr=str.substr(1,3);  >>bcd
console.log(subStr);

// 6.字符串切割
let arr = [1, 3, 5];
let str=arr.join('-');
console.log(str);  >>1-3-5
let str = "1-3-5";
let arr=str.split('-');
console.log(arr);  >>['1','3','5']

// 7.判断是否以指定字符串开头 ES6
let str = "www.it666.com";
let result=str.startsWith('www');
console.log(result);  >>true

// 8.判断是否以指定字符串结尾 ES6
let str = "lnj.jpg";
let result=str.endsWith('png');
console.log(result);  >>true

// 9.字符串模板 ES6
//常规方法
let str = "";
let str = '';
//新方法:反引号
let str=`www.it666.com`;
console.log(str);  >>www.it666.com
console.log(typeof str);  >>string

let str =   "<ul>\n" +
            " <li>我是第1个li</li>\n" +
            " <li>我是第2个li</li>\n" +
            " <li>我是第3个li</li>\n" +
            "</ul>";
//上面的字符串每行都要有加号,换成反引号就不用加号了
let str = `<ul> <li>我是第1个li</li> <li>我是第2个li</li> <li>我是第3个li</li> </ul>`;

let name = "lnj";
let age = 34;
let str=`个人名字是${name},个人年龄是${age}`;
console.log(str);  >>个人名字是lnj,个人年龄是34
复制代码
基本数据类型和基本包装类型

1.有哪些基本数据类型?

  • 字符串类型 / 数值类型 / 布尔类型 / 空类型 / 未定义类型

2.经过字面量建立的基本数据类型的数据都是常量

3.常量的特色和注意点

  • 常量是不能被修改的
  • 每次修改或者拼接都是生成一个新的(每次拼接都会开辟新的存储空间,多了会带来性能问题,因此要少用)

4.基本类型特色

  • 没有属性和方法

5.对象类型的特色

  • 有属性和方法

6.之前之因此可以访问基本数据类型的属性和方法, 是由于在运行的时候系统自动将基本数据类型包装成了对象类型

  • String() / Number() / Boolean()
//修改常量
let str = "abc";
str[1] = "m";  
console.log(str);  // abc
let newStr=str.replace('b','m');
console.log(newStr); // amc

//修改拼接
let str1 = "www";
let str2 = "it666";
let str3 = str1 + str2;
console.log(str1);  //www
console.log(str2);  //it666
console.log(str3);  //wwwit666

//基本类型没有属性和方法
let str = "lnj";
str.age = 34;
str.say=function(){
    console.log('hello');
}
console.log(str.age); // undefined
str.say(); // str.say is not a function

//系统自动将基本数据类型包装成了对象类型
let str = "www.it666.com";
// let str = new String(str); //隐藏过程
console.log(str.length);
str.split(".");
复制代码
三大对象

JavaScript中提供三种自带的对象, 分别是"本地对象"/"内置对象"/"宿主对象"

什么是宿主?

  • 宿主就是指JavaScript运行环境, js能够在浏览器中运行, 也能够在服务器上运行(nodejs)

1.本地对象

  • 与宿主无关,不管在浏览器仍是服务器中都有的对象,就是ECMAScript标准中定义的类(构造函数)。
  • 在使用过程当中须要咱们手动new建立,例如:Boolean、Number、String、Array、Function、Object、Date、RegExp等。

2.内置对象

  • 与宿主无关,不管在浏览器仍是服务器中都有的对象,ECMAScript已经帮咱们建立好的对象。
  • 在使用过程当中无需咱们手动new建立,例如:Global、Math、JSON

3.宿主对象

  • 对于嵌入到网页中的JS来讲,其宿主对象就是浏览器, 因此宿主对象就是浏览器提供的对象
  • 包含: Window和Document等
  • 全部的DOM和BOM对象都属于宿主对象

4.自定义对象

  • 咱们本身编写的类建立的对象
Math内置对象
  • Math.floor() 向下取整
    • 直接砍掉全部的小数位就是向下取整
let num = 3.9;
let value=Math.floor(num);
console.log(value);  //3
复制代码
  • Math.ceil() 向上取整
    • 只要有小数位就会给整数位+1, 而后砍掉全部小数位
let num = 3.9;
let value=Math.ceil(num);
console.log(value);  //4
复制代码
  • Math.round() 四舍五入
    • 和小学数学同样, 若是小数位满5就会进1
let num = 3.5;
let value=Math.round(num);
console.log(value);  //4.0
复制代码
  • Math.abs() 绝对值
    • 和小学数学同样, 统一变为正数
let num = -3;
let value=Math.abs(num);
console.log(value);  //3
复制代码
  • Math.random() 生成随机数
    • 会生成一个0~1的随机数, 可是不包括1
let value = Math.random();
console.log(value);
// 需求: 要求生成一个1~10的随机数
function getRandomIntInclusive(min,max){
    min=Math.ceil(min);
    max=Math.floor(max);
    return Math.floor(Math.random()*(max-min+1))+min;  
}  //含最大值,含最小值,从MDN复制过来
let value=getRandomIntInclusive(1,10);
console.log(value);
复制代码
相关文章
相关标签/搜索