JS简介

JS历史

  • JavaScript 诞生于 1995 年。NetSpace发布Netspace Navigator2浏览器,提供了免费开发工具LiveScript,设计的主要目的是处理以前由服务器端语言负责的一些输入验证操作。在人们普遍使用电话拔号上网的年代,能够在客户端完成一些基本的验证任务绝对很不容易。因为Java流行,所以改名为JavaScript。这个就是JavaScript1.0版本。
  • 因为JavaScript很受欢迎,Netspace Navigator3浏览器发布了JavaScript1.1版本。不久IE3也加入的脚本编程的功能,为了避免纠纷,命名为JScript。
  • 1997年,ECMA以JavaScript1.1为基础制定了脚本语言标准:ECMA-262,并命名为ECMAScript。浏览器厂商使用ECMAScript作为各自的JavaScript实现的规范标准。

ECMAScript

  • 1997年,ECMA发布262号标准文件(ECMA-262)第一版,规定了脚本语言的实现标准,并将这种标准命名为ECMAScript,这个就是ES1.0版本。

  • ECMAScript是JavaScript语言的规范标准,JavaScript是ECMAScript的一种实现方式。在一些语境中是可以互换的。ECMAScript规定了语言的组成部分:语法、类型、语句、关键字、保留字、操作符、对象。

ECMAScript版本

  1. 1998年6月, ECMAScript2.0版发布
  2. 1990年12月, ECMAScript3.0版发布,并成为 JavaScript的通用标准,获得广泛支持
  3. 2007年10月, ECMAScript4.0版草案发布,对3.0版做了大幅升级。由于4.0版的目标过于激进各方对于是否通过这个标准产生了严重分歧,2008年7月,ECMA中止ECMAScript4.0的开发,将其中涉及现有功能改善的一小部分发布为ECMAScript3.1.不久, ECMAScript3.1改名为 ECMAScript5
  4. 2009年12月, ECMAScrip5.0版正式发布
  5. 2011年6月, ECMAScript 5.1版发布
  6. 2013年12月, ECMAScrip6版草案发布2015年6月, ECMAScript6发布正式版本,并更名为 ECMAScript2015。目前最新版本为 ECMAScript2019,于2019年7月正式发布

为什么学习JS

  • 所有主流浏览器都支持 JavaScript。

  • 目前,全世界大部分网页都使用 JavaScript。

  • 它可以让网页呈现各种动态效果。

  • 做为一个 Web 开发师,如果你想提供漂亮的网页、令用户满意的上网体验,JavaScript 是必不可少的工具

  • 使用场景
    • WEB 前端
      • 图形处理
      • PDF生成
      • 图形界面
      • 各种测试工具
      • 视频和音频播放和处理
      • 通信
      • 多人协作
    • 后端
      • Node
      • 开发框架Express/ThinkJS/Clouda
      • 博客系统:Ghost/hexo
      • 基于 Node 的前端自动化工具:Grunt/Gulp
    • Hybrid App
    • 游戏
      • 世界上最流行的 2D 游戏引擎之一 Cocos2d 和最流行的 3D 游戏引擎之一 Unity3D 均支持 JS 开发游戏。

JavaScript 概念

JavaScript是一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,在HTML网页上使用,用来给HTML网页增加动态功能。

  • 直译式:它不需要经过编译器先行编译为机器码,之后直接在 CPU 中运行。直译式语言需要通过解释器,在运行期动态直译。
  • 脚本语言: JavaScript是在程序的运行过程中逐行进行解释,只在被调用时进行解释或编译。
  • 动态类型:变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。
  • 弱类型:数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值
  • 基于原型:只有对象,没有类;对象继承对象,而不是类继承类。“原型对象”是基于原型语言的核心概念。原型对象是新对象的模板,它将自身的属性共享给新对象。一个对象不但可以享有自己创建时和运行时定义的属性,而且可以享有原型对象的属性

JS构成

完整的JS由一下几个构成

  • ECMAScript:语言核心部分
  • 文档对象模型(DOM)网页文档操作标准,HTML应用程序编程接口(API),DOM把整个文档映射成一个树形节点结构,方便JS脚本快速访问和操作。
  • 浏览器对象模型(BOM) 客户端和浏览器窗口的操作基础,使用BOM可以对浏览器窗口进行访问和操作,如移动窗口,访问历史记录等等,没有规范,但是所有浏览器默认支持

JS初步使用

JS的引入

<script>标签

  • 向 HTML 页面中插入 JavaScript 的主要方法,就是使用*<script>*元素

  • 按照传统的做法,所有<script>元素都应该放在页面的<head>元素中

  • 但是现在脚本一般都写在 body 元素紧接着关标签之上

    • 浏览器解析HTML文档的时候,将根据文档流从上倒下逐行解析和显示。JS是HTML组成的一部分,因此JS脚本的执行顺序也是根据<script>书写位置决定的
    • 作为最佳实践,我们会在关闭body标签前引入JavaScript代码。这样浏览器就会在加载脚本之前解析和显示HTML,有利于提升页面的性能
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS的引入</title>
</head>
<body>
    <script>
        alert(1);
    </script>
    <script src="./demo1.js"></script>
</body>
</html>

<script>元素属性

  • src :可选。表示包含要执行代码的外部文件。
  • type :可选 一般为 text/javascript。现代浏览器默认脚本类型是JavaScript,因此可以省略type

外部JS文件

  • JS程序不仅可以直接写在HTML文档中,也可以放在JavaScript文件中。后缀名是.js。使用任何文本编辑器都可以编辑。
  • JS文件不能够单独运行,需要使用 <script>标签导入到网页中。
  • 定义src属性的<script>标签不应该再含有JavaScript代码,否则只会下载并执行外部JavaScript文件,嵌入代码被忽略。
<script src="./demo1.js"></script>

延迟执行JS-defer

  • <script>标签有一个布尔型属性defer,这个属性的用途是表明脚本在执行时不会影响页面的构造,也就是说,脚本会被延迟到整个页面都解析完成后再运行。
  • 因此在script元素中设置defer属性,相当于告诉浏览器立即下载,但是延迟执行
  • 如果页面中有多个延迟脚本,那么第一个延迟脚本会先于第二个延迟脚本执行,而这些脚本会先于DOMContentLoaded事件执行
  • 适用于外部JS文件,不适用于script标签包含的脚本
//外部引入的js文件
alert("外部引入js+defer");
<!-- 
    看运行结果
        1.head引入js
        2.body末尾引入js
        3.外部引入js+defer
-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./01.js" defer></script>
    <script>
        alert("head引入js")
    </script>
</head>
<body>
    <script>
        alert("body末尾引入js")
    </script>
</body>
</html>

异步加载JS文件-async

  • 在默认情况下,网页都是同步加载外部 JavaScript文件的,在引入外部js文件时会阻塞dom的执行,为此在html4.01为script标签引入了async属性
  • 现在可以为<script>标签设置 async属性,让浏览器异步加载 Javascript文件,即表示应该立即下载脚本,但不应妨碍页面汇总的其它操作。只对外部脚本文件有效。
  • 异步脚本不要在加载期间修改DOM,异步脚本语言一定会在页面的load事件前执行,但可能会在DOMContentLoaded事件(DOM渲染完成的监听事件)触发之前或之后执行。
  • 因为是下载完立即执行,不能保证多个加载时的先后顺序,因此确保异步脚本之间互不依赖
<!-- 以下代码在network中测试可见 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.css" rel="stylesheet">
    <link href="http://cdn.staticfile.org/foundation/6.0.1/css/foundation.css" rel="stylesheet">
    <script async src="http://lib.sinaapp.com/js/angular.js/angular-1.2.19/angular.js"></script>
    <script async src="http://libs.baidu.com/backbone/0.9.2/backbone.js"></script>
    <script async src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
</head>
<body>
   <ul>
       li*1000个
   </ul>
    
</body>
</html>

async&defer

  • 没有设置任何属性的script

    HTML文件将被解析,直到脚本文件被下载为止,同时将停止解析,发起请求去提取script文件(如果是外部文件)。下载并执行完成后将在恢复解析HTML。​

  • 设置了defer属性

    设置defer属性会在HTML解析的同时下载script,并在完成HTML解析和script下载之后执行该文件,同时设置了defer的脚本会按照他们在文档里面出现的顺序执行。​

  • 设置了async属性

    设置async属性会在HTML解析期间下载script文件,并在完成下载后暂停HTML解析器以执行该文件。​

基础调试代码

alert()

  • JavaScript 语句是发给浏览器的命令。这些命令的作用是告诉浏览器要做的事情

  • alert() 语句让浏览器弹出一个窗口,窗口里的内容就是 alert() 中的内容

alert(125);
alert("大帅比");
var sex = "girl";
alert(sex);

console.log()

  • console.log可以打印出你想要看到的信息

  • 包含浏览器的警告和报错和脚本打印内容​

console.log("hello world");

document.write()方法

  • 可以向HTML输出流中插入你传入的内容,浏览器会按着HTML元素依次顺序依次解析它们,并显示出来。
  • 需要注意的是,如果在文档加载完成后(即HTML输出已完成),再使用document.write()方法来要求浏览器来解析你的内容,则浏览器就会重写整个document,导致最后的这个document.write()方法输出的内容会覆盖之前所有的内容​
document.write("hello");
document.write("world");
// 点击文档执行事件
document.onclick = function(){
    //当文档解析完成以后执行,会直接覆盖
    document.write("事件发生1");
    document.write("事件发生2");
}

JS基本词法

  • 区分大小写

    JavaScript严格区分大小写,为了避免输入混乱和语法错误,建议采用小写字符编写代码。在以下特殊情况下可以使用大写形式:

    • 构造函数的首字母建议大写
    • 如果标识符由多个单词组成,可以考虑使用骆驼命名法—除首个单词外,后面单词的首字母大写
  • 标识符

    • 标识符指的是变量、函数、属性的名字,或者函数的参数。

    • 标识符命名是有规范的

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

    • 不能含有空格 不能以关键字或保留字命名

      关键字
      break do instanceof typeof
      case else new var
      catch finally return void
      continue for switch while
      debugger* function this with
      default if throw delete
      in try top
      保留字
      abstract enum int short
      boolean export interface static
      byte extends long super
      char final native synchronized
      class float package throws
      const goto private transient
      debugger implements protected volatile
      double import public yield
      implements package public interface
      private static let protected
  • 注释

    注释就是不被解析的一串字符。 JavaScript注释有以下两种方法

    • 单行注释:

      //我是单行注释  快捷键ctrl+/
    • 多行注释:

      /*
          我是多行注释 快捷键ctrl+shift+/
      */
  • 语句

    • JS中的语句以一个分号 ; 结尾

      //有效的语句 可能会出问题--不推荐
      var sum = a + b
      //有效的语句--推荐
      var sum = a + b;
      //花括号结尾可以省略分号--推荐
      function sum(a,b){return a + b}
  • 变量

    • 变量相当于容器,值相当于容器内装的东西,而变量名就是容器上贴着的标签,通过标签可以找到变量,以便读、写它存储的值
    • 为一块内存区域起的代号,通过这个代号,程序就可以把对应类型的数据保存到这个内存区域来完成相关计算的要求
    • ECMAScript 的变量是松散类型(弱类型,动态类型)的,所谓松散类型就是可以用来保存任何类型的数据。换句话说, 每个变量仅仅是一个用于保存值的占位符而已
    • 使用变量可以方便的获取或者修改内存中的数据
  • 变量的使用

    • 语法: var关键字 + 变量名称 = 数据 。等号右边的数据给等号左边变量名进行赋值
    • 在一个var语句中,可以声明一个或多个变量,也可以为变量赋值,未赋值的变量初始化为 undefined(未定义)值。当声明多个变量时,应使用逗号运算符分隔
    • 在 JavaScript中,可以重复声明同一个变量,也可以反复初始化变量的值
    var name = "xiaowang";
    
    var sex = "女" , age = 18;
    var sex2 ;
    var sex3 , age2;
    var sex4 , age3 = 19;
    console.log(sex,age);
    
    var score = 100;
    score = 120;
    console.log(score);
    var score = 150;
    console.log(score);
  • JS变量类型的特点

    JavaScript是弱类型语言,对于变量类型的规范比较松散。具体表现如下。

    • 变量的类型分类不严谨、不明确,带来使用的随意性。
    • 声明变量时,不要求指定类型。
    • 使用过程不严格,可以根据需要自动转换变量类型。
    • 变量的转换和类型检查没有一套统一、规范的方法,导致开发效率低下
  • 变量污染

    定义全局变量有3种方式:

    • 在任何函数体外直接使用var语句声明。
  • 直接添加属性到全局对象上。在Web浏览器中,全局作用域对象为 window

    • 直接使用未经声明的变量,以这种方式定义的全局变量被称为隐式的全局变量。

    全局变量在全局作用域内都是可见的,因此具有污染性。大量使用全局变量会降低程序的可靠性,用户应该避免使用全局变量

  • 练习

    两个变量的值交换

    //普通的做法就是声明多一个临时变量tmp,进行数据交换过程中的缓存。这样的做法直观,易懂。但是,会增加内存的使用。
        var a = 1,
            b = 2,
            tmp;
     
        tmp = a;
        a = b;
        b = tmp;
    
    //算术运算,通过算术运算过程中的技巧,可以巧妙地将两个值进行互换。但是,有个缺点就是变量数据溢出。因为JavaScript能存储数字的精度范围是 -253 到 253。所以,加法运算,会存在溢出的问题。
        var a = 1,
            b = 2;
        
        a = a + b; // a = 3, b = 2
        b = a - b; // a = 3, b = 1
        a = a - b; // a = 2, b = 1
    
    //其他方法可以以后学习~

JS数据类型

数据类型-声明变量练习

// 声明一个变量a,值为:3
// 声明一个变量b,值为:null
// 声明一个变量c,值为:"Hello!"
// 声明一个变量d,值为:true
// 声明一个变量e,不赋值
// 声明一个变量f,值为""
var a = 3;
var b = null;
var c = "hello";
var d = true;
var e ;
var f = "";
var g = [1,2,3,4];//数组
var h = function(){};//函数

typeof 操作符

我们需要有一种手段来检测给定变量的数据类型:

  • typeof 就是负责提供这方面信息的操作符,格式为 typeof(a) typeof a ;
  • 有些时候 typeof 操作符会返回一些令人迷惑但技术上却正确的值
    • 把null归为Object类型,而不是作为特殊的值
    • 把函数归为Function类型,而不是Object类型的一个子类
//检测上一个案例的变量
console.log(typeof a);//number
console.log(typeof (b));//object
console.log(typeof (c));//string
console.log(typeof (d));//boolean
console.log(typeof (e));//undefined
console.log(typeof (f));//string
console.log(typeof (g));//object
console.log(typeof (h));//function  

数据类型分类

  • JS数据类型分为两种:简单的值(原始值)和复杂的数据结构(泛指对象)。
  • 简单的值包含字符串、数字、布尔值。还有两个特殊的值 null(空值)和undefined(未定义)
  • 对象包括狭义对象、数组和函数
数据类型         说明

null            空值、表示非对象

undefined       未定义的值,表示未赋值的初始化值

nunber          数字,数学运算的值

string          字符事,表示信息流

boolean         布尔值,逻辑运算的值

object          对象。表示复合结构的数据集

Undefined类型

什么是Undefined类型

  • Undefined类型只有一个值,即undefined。比如在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined。
  • 我们不会对一个值设置undefined,一般都是出现错误的时候,才会被我们打印出来

常见的Undefined环境

  • 变量被声明了,但没有赋值时,就等于undefined

    //打印a的时候,找到a了 但是找不到a的值,所以返回一个undefined
    var a;
    console.log(a);
    console.log(a+1);//undefined+1  计算不了
    
    //不声明b,直接使用b,js直接报错  ReferenceError(引用错误): b is not defined
    //说明完全没有找到b这个变量 代码报错停止运行
    console.log(b)
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined

    // 声明函数使用function关键字 函数没有调用是完全不执行的
    function add(a,b) {
        console.log(a)
        console.log(b)
        alert(a+b);
    }
    // 调用函数 函数名+()  传入参数
    add(1,2);
    
    //再次调用函数(没有给够参数)
    add(1);
  • 对象没有赋值的属性,该属性的值为undefined

    // 创建一个对象
    var yourGirlFriend={
        name:"lily",
        age:18,
        length:180
    }
    // console.log(yourGirlFriend.name);
    console.log(yourGirlFriend.color);
  • 函数没有返回值时,默认返回undefined

    function reduce(a,b) {
        // 函数只要不写return 就没有返回值
        alert(a-b);
        // return a-b;//返回a-b
    }
    // 调用函数
    var num1=reduce(4,2);
    console.log(num1)
    
    console.log(reduce(4,1));
  • 检测一个变量是否初始化,如果没有,就为其赋值

    var a;
    if(typeof a === 'undefined'){
        a = 10;
    }
    console.log(a);

Null类型

Null类型解释

  • null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。
  • 从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测null时会返回"object"的原因

常见的 null 环境

  • 作为函数的参数,表示该函数的参数不是对象

    function fn(a,b) {
        alert(a+b);
    }
    // 需要传递参数,但是我们暂时不想传递,或者不需要传递,那么我们可以传一个空对象null
    fn(null,null)
    
    /*ajax有个方法send(),send方法参数是你要向服务器传递的值
            但是get方法是在地址栏拼接值,所以不需要send传递,所以我们在send中写一个参数null,告诉他我不想在这里传*/
  • 作为对象原型链的终点

    //比如:"123"--->String--->Object--->null
  • 如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值

    var a = null;
        function fn1() {
            a=2;
        }
        fn1();
        console.log(a);

Undefined和Null

  • Undefined派生自Null,两者都是表示空缺值,转换成布尔值都是假值,可以相等
  • 但是Undefined和Null属于两种不同的类型
  • Undefined隐含着意外的空值,而Null隐含着意料之中的空值。因此设置一个变量、参数为空的时候,建议使用null而不是undefined

Number类型

什么是Number类型

数字(Number)也称为数值或者数

  • 整数,浮点数值(数字直接量)
  • 八进制数(010、025)
  • 十六进制数(0xa、0x1c)
  • 二进制数
  • 科学计数法
  • Number.MIN_VALUE
  • Number.Max_VALUE
  • Infinity
  • NaN

整数,浮点数值(数字直接量)

当数字直接出现在程序中时候,被称为数字直接量 JS所有的数字都是以64位浮点数存储,所以2和2.0是同一个数字

var num1 = 19;//整数
console.log(num1);

var num2 = 18.11112;//浮点数
console.log(num2);

八进制数

数字前加一个0,代表8进制数

var num3 = 010;//数字前加一个0 代表8进制数
console.log(num3);//打印的是十进制  8

十六进制数

数字前加上0x,代表十六进制数

var num4 = 0xff;//数字前加上0x 代表十六进制数
console.log(num4);//255

二进制数(支持性不好,谨慎使用)

数字前加上0b,代表二进制数

var num5 = 0b11;//数字前加上0b 代表二进制数
console.log(num5);//3

科学计数法

用E代表底数10,后边跟E的指数,可以是正负值

//用E代表底数10  后边跟E的指数 可以是正负值
var num5 = 1.2E-7;
console.log(num5);//1.2E-7
console.log(num5+1);//1.00000012   运算以后 变成直接量显示

最大值最小值

在js中数字也是有最大值和最小值的支持的,如果超过最大值或最小值,就可能计算有误

// 在js中数字也是有最大值和最小值的支持的,如果超过最大值或最小值,就可能计算有误
console.log(Number.MIN_VALUE);//5e-324  支持数字的最小值
console.log(Number.MAX_VALUE);//1.7976931348623157e+308 支持数字的最大值

Infinity

  • 计算超出范围会得到无穷大(infinity)或者无穷小(-infinity)

  • 分母为0会构成无穷大Infinity 或负无穷大-Infinity

  • 关于Infinity的运算, 无法计算 加减乘除一个数字都是Infinity,Infinity和Infinity计算,加法乘法为Infinity,其他为NaN

  • Infinity和自身相等 其他比较正常

// 分母为0会构成无穷大Infinity 或负无穷大-Infinity
var num6 = 5/0;
console.log(num6);//Infinity

var num7 = -5/0;
console.log(num7);//-Infinity

console.log(typeof (Infinity));//无穷大和无穷小都是属于number类型

// 关于Infinity的运算, 无法计算 加减乘除一个数字都是Infinity,Infinity和Infinity计算,加法乘法为Infinity,其他为NaN
console.log(Infinity + 1);//Infinity
console.log(Infinity - 1);//Infinity
console.log(Infinity - 1000000000000000000000);//Infinity
console.log(Infinity - Infinity);//NaN
console.log(Infinity * Infinity);//Infinity
console.log(Infinity + Infinity);//Infinity
console.log(Infinity / Infinity);//NaN

//Infinity和自身相等  其他比较正常
console.log(Infinity > 1);//true
console.log(Infinity < 1);//false
console.log(Infinity == 1);//false
console.log(Infinity > Infinity);//false
console.log(Infinity == Infinity);//true
console.log(Infinity < Infinity);//false
console.log(Infinity > -Infinity);//true

NaN

  • NaN,即非数值(Not a Number)是一个特殊的数值

  • 这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)

  • aN特点:无论和谁比较都是fasle 无论和谁计算都是NaN

var num8 = "小明"-1;
console.log(num8)//NaN

// NaN特点:无论和谁比较都是fasle  无论和谁计算都是NaN
console.log(NaN+1);//NaN
console.log(NaN>1);//fasle
console.log(NaN==1);//fasle
console.log(NaN<1);//fasle

isNaN方法的使用

  • isNaN方法检测一个值是不是非纯数字 , 如果非纯数字就返回true 如果是纯数字就返回false

  • 案例:点击检测的时候,判断是不是全部是数字

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>isNaN</title>
</head>
<body>
    请输入您的电话号码:
    <input id="ipt" type="text">
    <button id="btn">检测</button>
    <script>
        /*
        *   1、点击按钮 (给按钮绑定点击事件)
        *   2、获取input的值ss
        *   3、使用isNaN检测
        *   4、判断检测的值是true还是false
        *   5、如果是true则 输入的值不是纯数字 如果为false 则是纯数字
         */

        // 0、获取标签
        /*document(文档)
        . 成员访问  访问文档里的属性或方法
        get是获取
        Element是元素
        By是通过
        Id  id名  括号里不是变量 要加上引号*/
        // 得到值 需要用一个变量保存这个元素
        // 命名规则 凡是获取DOM的元素的变量名 同意o开头,后边第一个字母大写
        var oIpt = document.getElementById("ipt");
        var oBtn = document.getElementById("btn");

        // 1、当点击btn的时候  onclick是点击事件  后边把一个匿名函数赋值给oBtn.onclick
        // 这个时候,当点击btn的时候,函数就会执行
        oBtn.onclick=function () {
            //2 获取input的值  并用变量保存
            var userCon = oIpt.value;
            // 3、判断是否纯数字 isTrue为true 则包含其他字符  为false则纯数字
            var isTrue = isNaN(userCon);
            //4、判断并执行
            //     如果isTrue为true 则弹出请重新输入,否则弹出输入正确
            if (isTrue == true) {
                alert("请重新输入");
            }else{
                alert("输入正确");
            }
        }



        /*// 上边代码优化如下
        var oIpt = document.getElementById("ipt");
        var oBtn = document.getElementById("btn");
        oBtn.onclick=function () {
            if (isNaN(oIpt.value)) {
                alert("请重新输入");
            }else{
                alert("输入正确");
            }
        }*/
    </script>
</body>
</html>

浮点数溢出

  • 执行数值计算,要防止浮点数溢出,例如 0.1+0.2不等于0.3
  • 因为js执行二进制浮点数算术标准而导致的问题。
  • 解决方法:浮点数的证书运算是精确的,所以消除表现的问题可以通过指定精度来避免。比如(1+2)/10

类型转换之Number方法

Number()方法 将其他类型转换成number类型 Number方法会返回一个转换后的值

// 1、Number转  数字转数字  还是原来的值

// 2、字符串转数字
console.log(Number(""));//0 空字符串-->0
console.log(Number("   "));//0 都是空格的字符串-->0
console.log(Number("123"));//0 纯数字的字符串-->相对应的数字
console.log(Number("1a23"));//0 非纯数字的字符串-->NaN


//3、布尔值转数字
console.log(Number(true));//1  true-->1
console.log(Number(false));//0  false-->0

// 4、undefined转数字
console.log(Number(undefined));//NaN  undefined-->NaN

// 5、null转数字
console.log(Number(null));// 0   null--->0

// 6、object(数组和对象)转数字
console.log(Number([]));//0 空数组-->0
console.log(Number([1,2,3]));//NaN 一般非空数组-->NaN
console.log(Number([1]));//1 数字只有一个值,并且是数字-->当前数字
console.log(Number(["1"]));//1 数字只有一个值,并且是数字值的字符串-->当前数字
console.log(Number(["a"]));//0 数字只有一个值,并且是非数字-->NaN

console.log(Number({}));//NaN  空对象-->NaN
console.log(Number({name:"lily"}));//NaN  非空对象-->NaNz

类型转换之parseInt()

parseInt是一个全局方法,它可以把值转换为整数

  • 第1步,先解析位置0处的字符,如果不是有效数字,则直接返回 NaN。
  • 第2步,如果位置0处的字符是数字,或者可以转换为有效数字,则继续解析位置1处的字符,如果不是有效数字,则直接返回位置0处的有效数字。
  • 第3步,以此类推,按从左到右的顺序,逐个分析每个字符,直到发现非数字字符为止。
  • 第4步,parseInt()将把前面分析合法的数字字符全部转换为数值并返回。

注意: 浮点数中的点号对于parseInt来说属于非法字符,因此不会转换小数部分值。 如果是以0开头的数字字符串,则parseInt()不会把它作为八进制数字处理 如果是以0x 开头的数字字符串,则 parseInt()会把它作为十六进制数字处理:先把它转换为十六进 制数值,然后再转换为十进制的数字返回。

parsInt也支持基模式,可以把不同进制的数字字符串转换为整数

console.log(parseInt(123));//123
console.log(parseInt("a123"));//NaN
console.log(parseInt("1a123"));//1
console.log(parseInt("10a23"));//10
console.log(parseInt("100px"));//100
console.log(parseInt(12.3));//12
console.log(parseInt("0xa"));//12

console.log(parseInt(null));//NaN
console.log(parseInt(true));//NaN


/*
    * parseInt 支持基模式,把不同进制的数字字符串转换为整数
* */
var a = "abc123";
console.log(parseInt(a,16));//11256099 把a当成16进制,转化为10进制输出
var b = "111";
console.log(parseInt(b,2));//7

console.log(parseInt(5,3));//NaN  因为3进制没有5这个字符

// 特殊情况如下:
console.log(parseInt(1,1));//NaN
console.log(parseInt(0,1));//NaN
console.log(parseInt(0,0));//0

类型转换之parseFloat()

  • parseFloat()也是一个全局方法,它可以把值转换成浮点数,即它能够识别第一个出现的小数点,而第二个小数点视为非法。解析过程和parseInt相同。

  • parseFloat()的参数必须是十进制的字符串,对十六进制和八进制前的0进行忽略或返回0。

console.log(parseFloat(123));//123
console.log(parseFloat(12.3));//12.3
console.log(parseFloat("12.3.3"));//12.3
console.log(parseFloat("a12.1"));//NaN

类型转换之乘号运算符

如果变量乘以1,则变量会被JS自动转换成数值,如果无法转换成合法数值,则返回NaN

/*
    *   如果说变量乘以1  变量就会被自动隐式转换为数字类型,如果转不了就变成NaN
* */
var a = "1";
console.log(a * 1);//number类型的  1

var b = "1a";
console.log(b * 1);//NaN


/*
    *   减法也可以
* */
var c = "1";
console.log(c - 0);//number类型的  1

var d = "1a";
console.log(d - 0);//NaN


/*除1也可以*/
var e = "1";
console.log(e / 1);//number类型的  1

var f = "1a";
console.log(f / 1);//NaN    

string类型

JavaScript字符串(String)就是由零个或多个Unicode字符组成的字符序列。零个字符表示空字符串。

字符串直接量

  • 子行串必须包含在单引号或双引号中
  • 如果字符串包含在双引号中,则字符串内可以包含单引号;反之,也可以在单引号中包含双引号
  • 在ECMAScript 3中,字符串必须在一行内表示,换行表示是不允许的,如果要换行显示字符串,可以在字符串中添加换行符(\n)
  • 在ECMAScript 5中,字符串允许多行表示.实现方法:在换行结尾处添加反斜杠(\).反斜杠和换行符不作为字符串直接量的内容
  • 在字符串中插入特殊字符,需要使用转义字符,如单引号,双引号等
  • 字符串中每个字符都有固定的位置.第1个子符的下标位置为0,第2个字符的下标位置为1...···以此类推,最后一个字符的下标位置是字符串长度减1
var str1 = '093284yrc091708)(*&(^&(*&T';
var str2 = "kajhx  askjh &*(";
var str3 = `9287O&*b`;
var str4 = "小王他妈妈说:'他要把翠花嫁给我'";
console.log(str4);

var str5 = "今天天气\n真好"
console.log(str5);

var str6 = "今天天气\
    真好";
console.log(str6);  

转义字符

  • 转义字符是字符的一种间接表示方式。在特殊语境中,无法直接使用字符自身

    var str = "请看\"这个是一个双引号";
    console.log(str);//请看"这个是一个双引号
  • 一些字符加上反斜杠后会表示特殊字符,而不是原字符本身,这些特殊转义字符被称为转义序列

    \0 Null字符(\u0000)

    \b 退格符(\u0008)

    \t 水平制表符(lu0009)

    \n 换行符(lu000A)

    \v 垂直制表符(\u000B)

    \f 换页符(\u000C)

    \r 回车符(\u000D)

  • 如果在一个正常字符前添加反斜杠,JavaScript会忽略该反斜杠

    var str = "小明妈妈说:\"今天天气真好\"";
    console.log(str);//小明妈妈说:"今天天气真好"
    var str2 = "小明妈妈说:\"\今\天\天\气\真\好\"";
    console.log(str2);//小明妈妈说:"今天天气真好"
    var str3 = "看我斜杠:\\"
    console.log(str3);//看我斜杠:\

字符串操作

  • 借助String类型的原型方法,可以灵活操作字符串(后面各章节中详细介绍)

  • 在JavaScript中,可以使用加号(+)运算符连接两个字符串

  • 使用字符串的length属性获取字符串的字符个数(长度)

  • 在ES5中,字符串可以作为只读数组使用,可以通过中括号运算符添加下标访问某一个值。下标从0开始,最大位置的下标是length-1

    var str = "老师说";
    var say = "你好啊";
    console.log(str+say);//老师说你好啊
    console.log(str + 1);//老师说1
    console.log(str + 1);//老师说1
    console.log(1 + 1);//2
    console.log("1" + 1);//11
    console.log(1 + "1");//11
    var str1 = "今天是个好天气123 b5";
    console.log(str1.length);//13
    console.log(str1[0]);//今
    //获取最后一个字符
    console.log(str1[str1.length-1]);//5

字符串小练习

用户提交账号密码的时候,判断是否正确 已知账号是lipeihua 密码是1234561

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>字符串练习</title>
</head>
<body>
<!--
    用户提交账号密码的时候,判断是否正确 已知账号是lipeihua  密码是1234561
-->
请输入账号名:
<input type="text" id="user" autofocus>
请输入密码:
<input type="password" id="pass">
<button id="btn">登录</button>
<script>
    /*
    * 1.获取元素
    * 2.点击事件
    * 3.判断账号是否正确(点击以后可以直接获取账号密码)
    * 4.判断密码是否正确
    * 5.如果不正确 清空重新输入  正确则返回登陆成功
    * */
    var oUser = document.getElementById("user");
    var oPass = document.getElementById("pass");
    var oBtn = document.getElementById("btn");

    oBtn.onclick = function () {
        //用userUser 保存点击的时候,用户输入的用户名
        var userUser = oUser.value;
        //用userPass 保存点击的时候,用户输入的密码
        var userPass = oPass.value;

        // 判断
        if(userUser == "lipeihua"){
            if (userPass == 1234561) {
                alert("送你一个小发发");
            }else{
                alert("小伙子 你的密码不对哦")
                oPass.value = "";
                oPass.focus();
            }
        }else{
            alert("用户名错误");
            // userUser = "";//千万不要只改变变量 因为这个只是对变量重新赋值,没有操作value值
            oUser.value = "";

            // 让表单获取焦点
            oUser.focus();
        }

    }

</script>
</body>
</html>

String方法

String方法是可以将其他类型转换成字符串类型

//1.null类型的转换
console.log(String(null));//字符串的 'null'

//2.undefined转换
console.log(String(undefined));//字符串的'undefined'

//3.number类型的转换
//转换规则:普通数字直接变成字符串  其他进制先转换成10进制然后在转换成相应的字符串 无穷大无穷小NaN都直接变成字符串
console.log(String(123));//'123'
console.log(String(-123));//'-123'
console.log(String(010));//'8'
console.log(String(0xff));//'255'
console.log(String(4E-5));//'0.00004'
console.log(String(Infinity));//'Infinity'
console.log(String(-Infinity));//'-Infinity'
console.log(String(12.3));//'12.3'
console.log(String(NaN));//'NaN'

//4.布尔值的转换
console.log(String(true));//'true'
console.log(String(false));//'false'

//5.对象的转换
console.log(String([]));//空字符串 ''
console.log(String([1]));//'1'
console.log(String([1,2,3]));//'1,2,3'
console.log(String({}));//[object object]
console.log(String({name:"lily"}));//[object object]

toString()方法

  • 我们的代码中有+(加号)运算符等情况下,它在这种情况下(字符串 + 其它什么东西),会调用toString()方法,将其它类型的东西转化为字符串,再和原始字符串拼接成一个字符串

  • 除了null和undefined之外,其他的类型(数值、布尔、字符串、对象)都有toString()方法,它返回相应值的字符串表现(并不修改原变量)。

  • 每个对象都有一个toString()方法。

  • 当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。

      (1).toString()      // "1"
    
      [1,2].toString()    // "1, 2"
    
      ({}).toString()     // [object Object]
    
      true.toString()     // "true"
    
      null.toString()     // Uncaught TypeError: Cannot read property 'toString' of null
    
      undefined.toString()  // Uncaught TypeError: Cannot read property 'toString' of null

Boolean类型

布尔类型仅包含两个固定的值:truefalse。其中true代表真,false代表假。

在一些判断等操作中,需要使用布尔值

类型转化之Boolean方法

//1、null
console.log(Boolean(null));//false

//2.undefined
console.log(Boolean(undefined));//false

//3.number
//数字转布尔值   非0为true  0为false NaN为false
console.log(Boolean(123));//true
console.log(Boolean(-123));//true
console.log(Boolean(0));//false
console.log(Boolean(1.23));//true
console.log(Boolean(NaN));//false
console.log(Boolean(Infinity));//true
console.log(Boolean(010));//true
console.log(Boolean(0xa));//true


//4.string

//空为false  非空为true
console.log(Boolean("123"));//true
console.log(Boolean(""));//false
console.log(Boolean("    "));//true


//5.object
//对象类型都转换成true
console.log(Boolean([]));//true
console.log(Boolean([1,2,3]));//true
console.log(Boolean([0]));//true
console.log(Boolean({}));//true
console.log(Boolean({name:"lily"}));//true

类型转化之双重逻辑非

一个逻辑非运算符(!)可以把值转换为布尔值并取反,两个就是转换成正确的布尔值

console.log(!!0);   

Boolea练习

点击按钮的时候,当input为空,则把input边框变红,否则是黑的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boolean类型</title>
    <style>
        #ipt{
            border:1px solid #000;
        }
    </style>
</head>
<body>
<input type="text" id="ipt">
<button id="btn">按钮</button>
<script>
    /*
    * 点击按钮的时候,当input为空,则把input边框变红,否则是黑的
    * */
    var oBtn = document.getElementById("btn");
    var oIpt = document.getElementById("ipt");
    oBtn.onclick = function () {
        var oIptValue = oIpt.value;
        //第一种判断 oIptValue == ""
        //第二种判断 oIptValue.length == 0
        //第三种判断 直接使用 if可以隐式转换的
        if(oIptValue){//oIptValue为非空
            oIpt.style.borderColor = "#000";
        }else{//oIptValue为空
            // 改变元素的行内样式
            oIpt.style.borderColor = "red";
        }
    }
</script>
</body>
</html>

JS运算符

运算符是根据特定的规则对操作数执行运算

JS共定义了47个运算符。

逗号操作符

  • 逗号运算符是二元运算符,它能够先执行运算符左侧的操作数,然后再执行右侧的操作数,最后返回右侧操作数的值。

    //逗号操作符执行计算总会返回最后一项
    var num4 = (4,5,6,7,8);
    console.log(num4);
    
    //i < 2,j < 4  前边为false 后边为true  这条语句仍然返回true  因为是逗号操作符总会返回后边的值
    for (var i = 0,j = 0; i < 2,j < 4; i++,j++) {
        console.log(i+j);
    }
    
    //i < 2,j < 4  前边为true 后边为false  这条语句仍然返回false  因为是逗号操作符总会返回后边的值
    for (var i = 0,j = 0; i < 4,j < 2; i++,j++) {
        console.log(i+j);
    }
  • 逗号运算符可以实现连续运算,如多个变量连续赋值

    var a=1,b=2,c=3,d=4;

赋值操作符

  • 赋值运算符左侧的操作数必须是变量、对象属性或数组元素。也称为左值

  • 赋值运算有以下两种形式:

    • 简单的赋值运算(=):把等号右侧操作数的值直接赋值给左侧的操作数,因此左侧操作数的值会 发生变化。

    • 附加操作的赋值运算:赋值之前先对右侧操作数执行某种操作,然后把运算结果复制给左侧操作 数 比如 +=  -+ *=  /=  %=

//赋值运算符的结合性是从右向左,最右侧的赋值运算先执行,然后再向左赋值,以此类推,所以连续赋值运算不会引发异常
var a = b = c = d = e = f =100;//连续赋值

//常见错误:
var c = 3;
//等于判断  写成了一个等号,这个时候就一直为true  所以判断是否成立都会执行
if (c = 4) {
    alert('hello');
}

乘性操作符

  • 乘性操作符分为 乘法 除法 求模(取余)

  • 乘性操作符计算 会先把两个值转换成number类型 然后再计算

    //乘法:
    console.log(3 * 3)//9
    console.log(-3 * 3)//-9
    console.log(-3 * -3)//9
    console.log(null * -3)//0
    console.log(undefined * -3)//NaN
    console.log(undefined * null)//NaN
    console.log(undefined * "123")//NaN
    console.log("10" * "123")//1230
    console.log("10" * 123)//1230
    console.log("abc" * 123)//NaN
    console.log(true * 123)//123
    console.log(true * false)//0
    console.log([] * false)//0
    console.log([] * [])//0
    console.log([] * {})//NaN
    console.log([] * {name:"lily"})//NaN
    console.log([2] * {name:"lily"})//NaN
    console.log([2] * {})//NaN
    console.log([2] * [3])//6
    console.log([2,3] * [3])//NaN
    console.log(Infinity * [3])//Infiity与任何相乘都是Infiity或-Infiity
    console.log(Infinity * Infinity)//Infinity
    console.log(Infinity * 0)//Infinity和0是NaN
    console.log(Infinity + Infinity)//Infinity
    console.log(Infinity - Infinity)//NaN
//除法
console.log(6 / 3);//2
console.log(-6 / 3);//-2
console.log(-6 / -3);//2 
console.log("abc" / 3);//NaN
console.log(true / 3);//0.333333
console.log(0 / 0);//NaN 0和0不能相除
console.log(5 / 0);//Infinity
console.log(Infinity / 0);//Infinity 被任意数除 都返回Infinity
console.log(Infinity / Infinity);//NaN
console.log([1] / Infinity);//0 当除数是Infinity的时候, 得到的结果是0 console.log(8000000000000000 / Infinity);//0
console.log([6] / 2);//3
//取余(模运算) 取余的符号只看被除数(前边的) 不看除数 
console.log(5 % 3);//2 
console.log(-5 % 3);//-2 
console.log(-5 % -3);//-2 
console.log(5 % -3);//2 
console.log('abc' % -3);//NaN 
console.log('5' % -3);//2 
console.log('5' % Infinity);//-5 
console.log(Infinity % -3);//NaN 
console.log([5] % -3);//2 
console.log({} % -3);//NaN
//减法操作符
//- 减法运算中,有一个不能转化成数字就返回NaN,Infinity与任意数相减都是Infinity
//- 技巧:使用值减去0 可以快速的把值转换成数字
console.log(5 - 2);//3
console.log("5" - 2);//3
console.log("a" - 2);//NaN
console.log([] - 2);//-2
console.log([5] - 2);//3
console.log([5,4] - 2);//NaN
console.log({} - 2);//NaN
console.log(true - 2);//-1
console.log(null - 2);//-2
console.log(undefined - 2);//NaN

加法操作符

概念

  • 加法操作符除了运算以外,还有字符串拼接的功能
  • 加法运算中,如果出现一个是字符串 那么就把另一个转换成字符串
  • 两个字符串相加,是将两个字符串链接再一起生成一个新的字符串
  • 对象之间相加,也是拼接字符串
  • 运算中如果只有数字和undefined、null,那么这将进行数字运算
console.log(1 + 1);//2

//加法运算中,如果出现一个是字符串  那么就把另一个转换成字符串
//两个字符串相加,是将两个字符串链接再一起生成一个新的字符串
console.log("1" + 1);//'11'
console.log("abc" + 1);//'abc1'
console.log("abc" + "abc");//'abcabc'
console.log(null + "abc");//'nullabc'
console.log(undefined + "abc");//'undefinedabc'
console.log([] + "abc");//'abc'

//空数组和对象拼接,都会把他们转换成字符串
console.log([] + {});//'[object object]'
//数组相加也是拼接字符串
console.log([] + []);//''

//两个对象拼接 也是转换成字符串
console.log({} + {});//[obejct object][object object]


console.log(undefined + null);//NaN
console.log(true + true);//2

加法操作符练习

var num1 = 1 + 2 + "a" + "b";//3ab
var num2 = "a" + 1 + 2 + "b";//a12b
var num3 = 1 + 2 + 3 + "a";//6a
var num4 = "a" + 2 + 3 + 4;//a234

var num5 = 1;
var num6 = 2;
alert("num5 + num6 = "+ num5 + num6);//'num5 + num6 = 12'

var name = "小明";
alert("我的名字是" + name);

加法操作符使用1

让用户输入两个值 计算两个值的和,当点击按钮时 让box的宽度和高度发生变化 值为两个值的和​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>加法操作符使用</title>
    <style>
        #box{
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
</head>
<body>
    请输入第一个值<input type="text" id="ipt1">
    请输入第二个值<input type="text" id="ipt2">
    <button id="btn">你点我呀</button>
    <div id="box">

    </div>

    <script>
        var oBox = document.getElementById("box");
        var oBtn = document.getElementById("btn");
        var oIpt1 = document.getElementById("ipt1");
        var oIpt2 = document.getElementById("ipt2");

        oBtn.onclick=function () {
            //1.点击按钮的时候  先获取两个输入框的值,并计算一个和
            //2.获取输入框的值是字符串,要传换成数字再进行加法运算求和
            var iptAdd = Number(oIpt1.value) + Number(oIpt2.value);
            console.log(iptAdd);

            //3.将得到的和赋值给box的宽和高
            // 对元素的style属性中的width属性进行赋值 宽和高都是有单位的 单位是px
            //这样的方法 设置的样式 都是行内样式
            oBox.style.width = iptAdd + "px";
            oBox.style.height = iptAdd + "px";
        }
    </script>
</body>
</html>

加法操作符练习2

每次点击按钮的时候,让con的内容的数值 每次加2​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>+=练习</title>
</head>
<body>
<div>
    <button id="btn">点击</button>
    <p id="con">0</p>

    <script>
        /*
         * 每次点击按钮的时候,让con的内容的数值 每次加2
         *
         * 1、获取元素
         * 2、点击事件
         * 3、获取con的值  innerHTML
         * 4、让con的值加2
         * 5、把con的值再给到con里 innerHTML
         */

        var oBtn = document.getElementById("btn");
        var oCon = document.getElementById("con");

        //2.绑定事件
        oBtn.onclick=function () {
            //3.获取con的值  获取元素的值 同样也是字符串  所以要转换成number
            var conCon = Number(oCon.innerHTML);
            //4.让con+2
            conCon += 2;
            //5.把计算好的值赋值到con中
            oCon.innerHTML = conCon;
        }
    </script>
</div>
</body>
</html>

学生信息录入系统

每次可以再输入框输入名字 然后点击以后 名字可以累加在p标签中 并且每个名字换行显示​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>+=练习</title>
</head>
<body>
<div>
    <h2>学生信息录入系统</h2>
    <input type="text" id="ipt">
    <button id="btn">点击</button>
    <p id="con">小明</p>

    <script>
        /*
         * 每次可以再输入框输入名字  然后点击以后 名字可以累加在p标签中  并且每个名字换行显示
         *
         * 1、获取元素
         * 2、点击事件
         * 3、获取ipt的值
         * 4、把ipt的值再给到con里
         */

        var oBtn = document.getElementById("btn");
        var oCon = document.getElementById("con");
        var oIpt = document.getElementById("ipt");

        //2.绑定事件
        oBtn.onclick=function () {
            //3.获取ipt的值
            var iptCon = oIpt.value;
            //4.把ipt的值累加到con中
            //获取con原来的值 拼接上iptcon(新的值)  然后赋值给con.innerHTML(con的内容)
            //我们可以在拼接赋值的时候 把标签当成字符串拼接进去   innerHTML是可以解析标签
            //oCon.innerHTML = oCon.innerHTML + "<br>" + iptCon;
            oCon.innerHTML += "<br>" + iptCon;
        }
    </script>
</div>
</body>
</html>

递增递减操作符

  • 递增(++)和递减(--)运算就是通过不断地加1或减1,然后把结果赋值给左侧,以实现改变自身结果的一种简洁方法
  • 递增和递减在运算之前都会试图转换值为数值类型,如果失败则返回 NaN。
  • 根据位置不同,可以分为4种运算方式
    • 前置递增(++n):先递增,再赋值
    • 前置递减(--n):先递减,再赋值
    • 后置递增(n++):先赋值,再递增
    • 后置递减(n--):先赋值,再递减
// 无论是  ++a;  还是 a++;  都是让 a每次加1,如果两个代码都是独立运行,那么两个代码没有区别
var a = 1;
// 让 a 加一
//a = a + 1;
//a += 1;
a ++;//a++是让a  每次加1
a ++;//a++是让a  每次加1
a ++;//a++是让a  每次加1
console.log(a);

var b = 1;
++ b;//让b每次加1
++ b;//让b每次加1
++ b;//让b每次加1
++ b;//让b每次加1
console.log(b);

//c++不是单独运行的时候,也就是有赋值操作的时候,那么是先执行赋值,然后再去加1
//c++ 就是先赋值 后加1
var c = 2;
var d = c ++;
console.log(c);//3
console.log(d);//2

//++c  先加一 后赋值
var c = 2;
var d = ++ c;
console.log(c);//3
console.log(d);//3

//其他练习
var a = 1;
b = a++ + (a++) + 1 + (++a) + (a++) + (++a) + a + (++a);
// b = 1 + 2 + 1 + 4 + 4 + 6 + 6 + 7;
console.log(a);//7
console.log(b);//31

关系运算

关系运算也称比较运算,需要两个操作数,运算返回值总是布尔值

大小比较

  • 比较大小关系的运算符有4个
    • >:如果第一个操作数小于第二个操作数,则返回true;否则返回 false
    • <=:如果第一个操作数小于或者等于第二个操作数,则返回 true;否则返回 false
    • >=:如果第一个操作数大于或等于第二个操作数,则返回true;否则返回 false
    • >:如果第一个操作数大于第二个操作数,则返回true;否则返回 false
  • 比较运算中的操作数可以是任意类型的值,但是在执行运算时,会被转换为数字或字符串,然后再 进行比较。如果是数字,则比较大小; 如果是字符串,则根据字符编码表中的编号值,从左到右逐个比 较每个字符
    • 如果两个操作数都是数字,或者一个是数值,另一个可以被转换成数字,则将根据数字大小进行比较
    • 如果两个操作数都是字符串,则执行字符串比较。
    • 如果一个操作数为NaN,或者被转换为NaN,则始终返回 false。
    • 如果一个操作数是对象,则先使用 valueOf取其值,再进行比较:如果没有valueOf方法,则 使用toString取其字符串表示,再进行比较
    • 如果一个操作数是布尔值,则先转换为数值,再进行比较。
    • 如果操作数都无法转换为数字或字符串,则比较结果为false
var num1 = 3;
var num2 = 4;
console.log(num1 < num2);//true
console.log(num1 <= num2);//true
console.log(num1 >= num2);//false
console.log(num1 > num2);//false


//数字和字符串相比  按照数字来比较
console.log(1 < 4);//true
console.log(1 < '4');//true
console.log(1 < 'a');//false  1 和 NaN在比较

//字符串和字符串比较 是按照每一位来比较的  先比较第一位 然后如果相等再比较第二位 等等等。。。
//字符串比较的是ascII码的大小  数字<大写字母<小写ß字母
console.log("14" < '4');//true
console.log("abc" < '4');//false
console.log("abc" < 'ab');//false

//null和字符串比 是按照数字相比的  null转换成了数字
console.log(null > 'abcd');//false
console.log(undefined < 1);//false
console.log(undefined < "abc");//false
console.log(undefined < null);//false

//两个{}相比 按照数字来比较  都是NaN  返回false
console.log({} > {});//fasle

//数组和对象比较  转换成了字符串比较  ""<'[obejct object]'
console.log([] < {});//true

//有数组没有数字  按照字符串比较
console.log([4] > [14]);//true


//布尔值和其他比较 都转换成数字比较
console.log(true > -3);//true
console.log(true > []);//true

//布尔值和对象比较 也是按照数字比较的,对象转换成了NaN
console.log(true == {});//fasle

相等和全等

  • 等值检测运算符包括4个:
    • ==:比较两个操作数的值是否相等
    • !=:比较两个操作数的值是否不相等
    • ===:比较两个操作数的值是否相等,同时检测它们的类型是否相同
    • !==比较两个操作数的值是否不相等,同时检测它们的类型是否不相同
  • 在相等运算中,应注意以下几个问题
    • 如果操作数是布尔值,则先转换为数值,其中false转为0,true转换为1.
    • 如果一个操作数是字符串,另一个操作数是数字,则先尝试把字符串转换为数字
    • 如果一个操作数是字符串,另一个操作数是对象,则先尝试把对象转换为字符串。
    • 如果一个操作数是数字,另一个操作数是对象,则先尝试把对象转换为数字。
    • 如果两个操作数都是对象,则比较引用地址。 如果引用地址相同,则相等; 否则不等。
    • NaN和任何值都不相等,包括自身
    • null和undefined值相等,但是是不同的数据类型
  • 在全等运算中,应注意以下几个问题
    • 如果两个操作数都是简单的值.,则只要值相等,类型相同,就全等
    • 如果一个操作数是简单的值.另一个操作数是复合型对象,则不全等
    • ;如果两个操作数都是复合型对象,则比较引用地址是香相同.

逻辑运算

逻辑运算又称布尔代数,也就是布尔值的算术运算。逻辑运算符包含:逻辑与(&&)、逻辑或(||)、逻辑非(!)

逻辑与运算

逻辑与(&&)运算是只有两个数都是true的时候,才会返回true(一假即假)

  • 第一步:计算第一个操作数
  • 第二步:如果第一个操作数转换成false,那么就会结束运算,直接第一个操作数
  • 第三步:如果第一个操作数返回true,则计算第二个操作数的值
  • 第四步:第二个操作数如果返回true 则逻辑与返回第二个操作数,否则返回第一个操作数
//假设用户输入的长度 在10 到20 之间  那么就成功
var userLen = 15;
//参照操作符优先级  比较操作符 大于 逻辑操作符
console.log(userLen > 10 && userLen < 20);

逻辑或运算

逻辑或运算(||),如果两个操作数都是true,或者其中一个是true的时候,会返回true。

  • 第一步:计算第一个操作数的值。
  • 第二步:检测第一个操作数的值。如果左侧的表达式的值可以转换为true,那么就会结束运算。直接返回第一个操作数的值
  • 第三步:如果第一个操作数可以转换为false,则计算第二个操作数的值。并返回第二个操作数的值
//小明的女朋友 有3个 或 5个是合法的  是其他的就不行
var mingGrilFri = 4;
console.log(mingGrilFri == 3 || mingGrilFri == 5);

逻辑非运算

逻辑非运算,作为一元运算符,直接放在操作数之前,把操作数的值转为布尔值,然后取反返回

//如果说用户输入的全部都是数字  则返回成功
// isNaN  判断传入的值 是否是纯数字  如果是纯数字则返回false  否则是true
var userNum = '15701665563';
if (isNaN(userNum)) {

}else{
    alert("成功")
}

//优化
if (!isNaN(userNum)) {
    alert("成功");
}

逻辑与和逻辑或的返回值不必是布尔值,但是逻辑非运算的返回值一定是布尔值

与或非练习

判断两个数,如果有一个是非数字,那么就提示用户,否则就计算两个数字的和

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>与或非练习</title>
</head>
<body>
<script>
    // 判断两个数,如果有一个是非数字,那么就提示用户,否则就计算两个数字的和
    var num1 = "3333";//input中拿到的值,只能是字符串
    var num2 = "4444";

    /*if (isNaN(num1) || isNaN(num2)){
        alert("两个数字中存在非数字")
    } else{
        alert(Number(num1) + Number(num2));
    }*/


    if (!isNaN(num1) && !isNaN(num2)){
        alert(Number(num1) + Number(num2));
    } else{
        alert("两个数字中存在非数字")
    }
</script>
</body>
</html>

短路原理

  • && || 遵循“短路”原理
  • &&中第一个表达式为 fasle 则不会处理下一个表达式;||第一个表达式为true则不会执行下一个
  • 当 || 时,找到为 true 的分项就停止处理,并返回该分项的值;否则执行完,并返回最后分项的值。
  • &&时,找到为false的分项就停止处理,并返回该分项的值
// 这种类似的题目,一般不考虑整体返回值,而是看前一句条件是否满足,然后看后一句代码是否执行
var a = 2; var b = 3;
(a < b)&&(a = 5);
console.log(a);//5

var a = 6; var b = 3;
(a < b)&&(a = 5);
console.log(a);//6

var a = 2; var b = 3;
(a < b)||(a = 5);
console.log(a);//2

var a = 6; var b = 3;
(a < b)||(a = 5);
console.log(a);//5

一元运算

一元运算符:只有一个运算数的运算符,比如++  --:自增(自减)、+(-):正负号等

var a = "5";
var b = 6;
console.log(Number(a));

console.log(typeof +a);//number

// 一元加法运算对数字没有影响  但是对其他类型却转换成了数字
console.log(+b);//6
console.log(+a + 2);//7
console.log(+"abc");//NaN
console.log(+[]);//0
console.log(+null);//0
console.log(+undefined);//NaN
console.log(+{});//NaN
console.log(+false);//0

// 一元减法 对数字没有加一个负数  对其他类型先转换成数字 然后添加一个负号
console.log(-a + 2);//-3

// if判断会自动把判断式转换成布尔值
var a = 0;
if(a){
    alert("成功")
}else{
    alert("失败")
}

字符串转数字总结

  • Number方法
  • 一元运算
  • 减0
  • parseInt 和 parseFloat
var a = "4";
// 第一种  Number方法
console.log(Number(a));//4

//第二种  一元运算
console.log(+a);//4

// 第三种 减0
console.log(a - 0);//4

// 第四种 parseInt 和 parseFloat
//parseInt对字符串检测,从第一位开始检测,直到检测到非数字为止,然后将检测到的数字取整
console.log(parseInt("100px"));;//100
console.log(parseInt("10abc10"));;//10
console.log(parseInt("abc10"));;//NaN
console.log(parseInt("10.533abc10"));;//10
console.log(parseInt("10.a001bc10"));;//10

//parseFloat对字符串检测,从第一位开始检测,直到检测到非数字为止 保留小数
console.log(parseFloat("100px"));;//100
console.log(parseFloat("10abc10"));;//10
console.log(parseFloat("abc10"));;//NaN
console.log(parseFloat("10.533abc10"));;//10.533
console.log(parseFloat("10.a001bc10"));;//10

// 效果:点击获取元素宽度  并每次加10
//1 获取元素的宽度(这种方法只能获取元素的行内样式)
var oBox = document.getElementById("box");
var oBtn = document.getElementById("btn");
var boxWidth = oBox.style.width;
console.log(boxWidth);//'100px'
// 2.对 btn 绑定点击事件
oBtn.onclick = function () {
    //3.让宽度的值每次加10
    boxWidth = parseInt(boxWidth)+10+"px";
    //4.赋值
    oBox.style.width = boxWidth;
}

三元运算符(三目运算符、条件运算符)

语法格式:b?x :y

  • b操作数必须是一个布尔型的表达式,x和y是任意类型的值
  • 如果操作数b的返回值是true,则执行x的操作数,并返回该表达式的值
  • 如果操作数b的返回值是false,则执行y的操作数,并返回该表达式的值
var num = true ? 3 : 2;
console.log(num);

// 定义变量a,然后检测a是否被赋值,如果被赋值,则使用该值,否则设置一个默认值
var a;
typeof a !="undefined"?a = a:a = 0;
console.log(a);

判断式的优化

以下四中方法书写判断式:如果a大于b 则弹出你好 否则 弹出滚蛋

// 如果a大于b  则弹出你好  否则 弹出滚蛋
// 方法1
if (a > b){
    alert("你好");
} else{
    alert("滚蛋");
}

// 方法2 三元运算
a > b ? alert("你好"):alert("滚蛋");

// 方法3 短路原理
a > b || alert("滚蛋");
a > b && alert("你好");

//方法4:
a > b && alert("你好")|| alert("滚蛋");

综合练习

变量 year 中存放的是年份值,判断变量是不是闰年,var year;

//年份能够被4整除且不能被100整除,或者年份能够被400整除
var year = 2016;
if (year % 4 == 0 && year % 100 != 0 || year % 400 ==0) {
    alert("是闰年")
}

JS语句

什么是语句

  • ECMA-262 规定了一组流程控制语句。语句定义了 ECMAScript 中的主要语法,语句通常由一个或者多个关键字来完成给定的任务。诸如:判断、循环、退出等。
  • 语句主要分为:声明、分支控制、循环控制、流程控制、异常处理等。

声明语句

声明语句包含三种:声明变量、声明函数和声明标签

// 声明函数 使用function关键字
function f() {}

// 声明变量
var a = 1;//有声明提升  作用域按照函数计算
let b = 2;//无声明提升  作用域按照代码块计算
const MAX = 100;//无声明提升  常量  不能被修改或者重新赋值

// 声明标签Label:
// 声明语句一般都是配合continue和break来使用的,在后边流程控制语句中讲解
for1:
for (var i = 0;i<3;i++){

}

调试语句

  • debugger语句用于停止JS
  • debugger语句可以放在代码的任何位置用以脚本中止执行,但是不会关闭任何文件或者删除变量,类似在代码中设置断点
for (var i = 0; i < 5; i++) {
    debugger;
    console.log(1);
}

if语句

单分支if

  • 书写格式:if (判断式){代码块}
  • 如果判断式为真,则执行代码块。
  • 判断式不一定要书写各种操作符,无论传递什么进去,if都会把它转换成布尔值 然后执行if代码
  • 一般有 ( 当。。。。的时候 否则。。。。的时候 )使用if语句
  • if判断语句的代码块是单句,我们可以省略大括号(但是js中不建议这样书写)​
//【单分支if】声明变量 a、b,且赋值 a 和 b 都等于5;当 a 等于 b 时,重新对 b 赋值为10,并弹出 b 的新值
var a = 5 , b = 5 ;
// 如果a不等于b  那么整个if就不会执行  因为没有else
// js是允许只书写if  不书写else 的
if ( a == b ){
    b = 10;
    alert(b);
}

双分支 else语句

  • else语句仅在if或者else/if语句的条件表达式为假的时候执行。
  • if(判断式){代码块1}else{代码块2}
  • 如果判断式为真,则执行语句1,否则将执行语句2​
//【双分支】声明变量 a赋值为100;当 a 大于 50 时,弹出“a真大”,否则弹出“a真小”
var a = 100;
if (a > 50){
    alert("a真大");
}else{
    alert("a真小");
}

//【双分支】y值和x值相关。X大于等于3时,y值为2。X小于3时,y值为1
var y = 4,x = 2;
if (x >= 3){
    y = 2;
}else{
    y = 1;
}

//【双分支】如果x大于y成立,则x值为y值加1,否则x值为y值减1
var x = 3,y = 4;
if (x > y){
    x = y + 1;
}else{
    x = y - 1;
}

//【双分支】如果x大于y或者x小于z成立,则x值为z值,否则x值为y值
var x = 1,y = 2,z = 3;
if (x > y || x < z) {
    x = z;
}else{
    x = y;
}

else if语句

  • else if语句 可以构成多分支
  • if (条件表达式) {语句1;} else if (条件表达式) {语句2;} ... else {语句3;}
// 【多分支】如果x值为3,y值为2,如果x值大于3,y值为1,如果x值小于3,y值为4
var x = 3,y = 4;
if (x == 3){
    y = 2;
}else{
    // x不等3的情况 进入else
    if (x >3){
        y = 1;
    } else{
        y = 4;
    }
}
//else if()是在上一次判断的基础上 在剩下的条件中继续判断
if (x == 3){
    y = 2;
}else if (x > 3){
    y = 1;
}else{
    y = 4;
}

//【多分支】x值为2,y值为1,如果x值比2小,y值为0,如果x值比10大,y值为10,如果x值小于4且大于3,y值为5,其他情况y为3
var x = 2, y = 1;
if (x < 2){
    y = 0;
}else if (x >10){
    y = 10;
}else if(x < 4 && x > 3){
    y = 5;
}else{
    y = 3;
}

if语句的练习

用户输入成绩 然后弹出成绩的等级​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>if语句的练习</title>
</head>
<body>
请输入成绩(数字):
<input type="text" id="ipt">
<p>等级:<span id="con"></span></p>
<script>
    /*
     * 用户输入成绩  然后弹出成绩的等级
     * 成绩是 >=90  A    >=80 <90  B  >=70 <80  C  >=60 <70  D     其他  Z
     */
    var oIpt = document.getElementById("ipt");
    var oCon = document.getElementById("con");
    oIpt.onchange = function () {
        // 获取表单的值,并转换成数值
        var userScroe = parseInt(this.value);
        if (userScroe >= 90){
            oCon.innerHTML = "A";
        }else if(userScroe >= 80){
            oCon.innerHTML = "B";
        }else if(userScroe >= 70){
            oCon.innerHTML = "C";
        }else if(userScroe >= 60){
            oCon.innerHTML = "D";
        }else{
            oCon.innerHTML = "Z";
        }
    }
</script>
</body>
</html>

switch语句

  • switch语句专门用来设计多分枝条件结构。与else/if多分支结构相比,switch结构更加简介,执行效率更高。

  • 语法如下:

    switch(表达式){

    case value1:语句1;break; case value2:语句2;break; ………… default:语句 }

    • switch语句根据表达式的支,一次与case后的表达式的value值进行比较。如果相等,则执行其后的语句段,只遇到break,或者switch语句结束才中止,如果不想等,则继续查找下一个case。
    • switch语句包含了一个可选的default语句(switch的异常处理),如果前边的case没有找到相等的条件,则执行default语句,它与else语句类似。
    • switch语句是使用全等(===)来检测两个只的相等与否。所以会有一个值类型的比较
    • case后可以是一个空语句,把多个条件放在一起检测
    • 在switch语句中,case子句只是指明了执行的起点,但是没有指明终点,如果case后没有break语句,就会发生连续执行的情况,而忽略后边case的条件限制。
var a = 6;
    switch (a) {
        case 1:
            alert(1);
            // break;
        case 2:
            alert(2);
            // break;
        case 3:
            alert(3);
            // break;
        case 4:
            alert(4);
            break;
        case 5:
        case 6:
        case 7:
            alert(8);
            break;
        default:
            alert(10)
    }
  • 练习

    用户输入成绩 然后弹出成绩的等级​

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>switch练习</title>
    </head>
    <body>
    请输入成绩(数字):
    <input type="text" id="ipt">
    <p>等级:<span id="con"></span></p>
    <script>
      /*
       * 用户输入成绩  然后弹出成绩的等级
       * 成绩是   100-->S    >=90  A    >=80 <90  B  >=70 <80  C  >=60 <70  D     其他  Z
       */
      var oIpt = document.getElementById("ipt");
      var oCon = document.getElementById("con");
      oIpt.onchange = function(){
          //获取用户输入的成绩
          var userScore = this.value;
          // 因为switchdo都是相等判断 所有我们都取 分数的第一位 然后判断相等 比如所有的90分以上  都是9开头
    
          userScore = parseInt(userScore / 10);
          switch (userScore) {
              case 10:
                  oCon.innerHTML = "S";
                  break;
              case 9:
                  oCon.innerHTML = "A";
                  break;
              case 8:
                  oCon.innerHTML = "B";
                  break;
              case 7:
                  oCon.innerHTML = "C";
                  break;
              case 6:
                  oCon.innerHTML = "D";
                  break;
              default:
                  oCon.innerHTML = "D";
          }
      }
    </script>
    </body>
    </html>

循环语句

在程序开发中,存在大量的重复性操作或计算,这些任务必须依靠循环结构来完成。JS提供了 while  for do/while三种类型的循环语句。

while语句

  • while语句是最基本的循环结构。语法格式如下:

    while(判断式){

    ​ 语句

    }

  • 当判断式为真的时候,执行语句。执行结束之后再次进行判断,直到判断式为假,才跳出循环。 ​

// 用while()循环来计算1+2+3 ... +98+99+100的值。

var i = 1;
var num = 0;
while(i <= 100){
    num += i;
    i ++;
}
console.log(num);

// 也可以在循环语句中添加增量
var i = 1;
var num = 0;
while(i++ <= 100){
    num += i;
}

//每次执行时,要让条件发生变化,否则将进入死循环
var a = 4;
while(a < 7){
    console.log(a);
    a ++ ;
}

// 将1-100之间  3的倍数找出来 并打印
var i = 1;
while(i <= 100){
    if (i % 3 == 0){
        console.log(i);
    }
    i++;
}

do/while语句

  • do/while与while循环非常相似,区别在与表达式的值是每次循环结束后去检查,而不是开始去检查。

    do{

    ​ 语句

    }while(判断式子)

  • 因次do/while循环能够保证至少执行一次循环。而while就不一定了。​

//基础使用
var a = 3;
do{
    console.log("hello");
}while(a < 1);

// 用do-while()循环来计算1+2+3 ... +98+99+100的值:
var i = 1;
var num = 0;
do{
    num += i;
    i ++;
}while(i <= 100);
console.log(num)

练习-计算年利率

用户输入一个钱数 年利率为1.05(去年的利息和本钱 是下一年的本金) 翻倍需要几年​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算年利率</title>
</head>
<body>
<input type="text" id="ipt">
<script>
    /*
    * 用户输入一个钱数  年利率为1.05(去年的利息和本钱 是下一年的本金)  翻倍需要几年
     */

    var oIpt = document.getElementById("ipt");
    var scale = 1.05;
    oIpt.onchange = function () {
        var userMoney = parseFloat(this.value);
        var fatalMoney = 2 * userMoney;
        var years = 0;

        //当用户的钱只要小于翻倍的钱,那么就让循环一直执行
        while(userMoney <= fatalMoney){
            // 循环体中,让本金每次执行都翻1.05倍,每一年循环一次
            userMoney *= scale;
            //每执行一次循环  让年数加1
            years ++;
        }
        alert("您的钱翻倍需要"+years+"年");

    }
</script>
</body>
</html>

练习-判断登录

用户输入的账号和密码,只要账号不是laowang ,密码不是 88888,我们就一直提示请重新输入,否则 提出输入成功

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>判断登录</title>
</head>
<body>
<script>
    // prompt是包含输入框  确认和取消的弹窗 用户输入的值就是这个prompt方法的返回值  如果用户没有输入或输入空  则返回null
    //prompt第一个参数 是提示用户的话

    //方法一
    //1、页面刚进入 先弹窗让用户输入账号名和密码
    var userName = prompt("请输入用户名");
    var passWord = prompt("请输入密码");
    //2、如果判断不正确 就一直让弹出输入账号名密码  所以可以考虑while循环
    while(userName != "laowang" || passWord != "88888"){
        // 3、密码输入错 或者  账号输入错误  就进入while执行 弹窗
        userName = prompt("请输入用户名");
        passWord = prompt("请输入密码");
    }
    alert("欢迎光临  老王")


    // 方法二: 递归调用
    var userName = prompt("请输入用户名");
    var passWord = prompt("请输入密码");
    //因为每次输入完都要判断  所以需要封装一个 函数
    function isLogin() {
        if (userName != "laowang" || passWord != "88888"){
            userName = prompt("请输入用户名");
            passWord = prompt("请输入密码");
            isLogin();//递归算法(在函数中调用自身)
        }else{
            alert("欢迎光临 老王 你是老王吧")
        }
    }
    isLogin();
</script>
</body>
</html>

for语句

  • for语句是一种更简洁的循环结构

    for(表达式1;表达式2;表达式3 ){

    ​ 语句1;

    ​ 语句2……;

    语句3

  • 表达式1在循环开始前无条件的求值一次,而表达式2在每次循环开始前求值。如果表达式2的值为真,则执行循环语句,否则终止循环。表达式3在每次循环后被求值,然后再次判断。

/*
    for循环执行顺序
    1、首先执行语句1
    2、执行判断式2 如果成立(返回true)则执行代码块  如果不成立(返回false) 整个循环结束
    3、如果判断式成立去执行了代码块,执行完代码块 会去执行语句3
    4、语句3执行完成后  再次去执行判断式2
    5、重复第二个步骤
*/

// 假设 弹出你好 总共弹出5次
for (var i = 0; i < 5; i++) {
    console.log("nei hao a");
}

// 练习:定义一个变量a=5,让a执行5次,每次a加3。然后弹出a最终的值。
var a = 5;
for (var i = 0; i <5 ; i++) {
    a += 3;
}
console.log(a);

for循环的练习

  • 求1到9的和
  • 求1到100的和
  • 求1-99中的单数的和
  • 求1-99中是3的倍数的数字和
  • 定义一个v,当v等于0时,v进行3次自加1。否则v进行4次自加2
  • 定义v=3;对变量i由0到3进行遍历;当i 等于2时,v自加2;否则v自加3
    //1、求1到9的和
    // 1+2+3+4+5+6+7+8+9
    // 定义一个变量保存和  默认是0
    var num = 0;
    for (var i = 1; i < 10; i++) {
        num += i;
    }
    console.log(num);


    // 2、求1到100的和
    var num = 0;
    for (var i = 1; i < 101; i++) {
        num += i;
    }
    console.log(num);


    // 3、求1-99中的单数的和
    var num = 0;
    for (var i = 1; i < 100; i+=2) {
        num += i;
    }
    console.log(num)


    // 4、求1-99中是3的倍数的数字和
    var num = 0;
    for (var i = 3; i < 100; i+=3) {
        num += i;
    }
    console.log(num)

    var num = 0;
    for (var i = 0; i < 100; i++) {
        if (i % 3 == 0){
            num += i;
        }
    }
    console.log(num)


    // 5、定义一个v,当v等于0时,v进行3次自加1。否则v进行4次自加2
    var v = 3;
    if(v == 0){
        for (var i = 0; i <3 ; i++) {
            v++;
        }
    }else{
        for (var i = 0; i <4 ; i++) {
            v += 2;
        }
    }

    // 6、定义v=3;对变量i由0到3进行遍历;当i 等于2时,v自加2;否则v自加3
    var v = 3;
    for (var i = 0; i < 4; i++) {
        if (i == 2){
            v += 2;
        }else{
            v += 3;
        }
    }

for循环的应用

对所有的li绑定点击事件 点击谁 让谁的背景颜色变成红色​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>for循环应用</title>
</head>
<body>
    <ul id="box">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
    <script>
        /*
         * 效果 对所有的li绑定点击事件  点击谁 让谁的背景颜色变成红色
         * 1、获取标签
         */

        // 1、获取标签
        var oBox = document.getElementById("box");
        var oLis = oBox.getElementsByTagName("li");

        /*oLis[0].onclick = function () {
            this.style.backgroundColor = "red";
        }
        oLis[1].onclick = function () {
            this.style.backgroundColor = "red";
        }
        oLis[2].onclick = function () {
            this.style.backgroundColor = "red";
        }
        oLis[3].onclick = function () {
            this.style.backgroundColor = "red";
        }
        oLis[4].onclick = function () {
            this.style.backgroundColor = "red";sd 
        }*/

       /* for (var i = 0; i < oLis.length; i++) {
            oLis[i].onclick = function () {
                this.style.backgroundColor = "red";
            }
        }*/


        /*
         * 效果 对偶数个数的(0 2 4 6 8 10)li绑定点击事件  点击谁 让谁的背景颜色变成红色
         * 1、获取标签
         */
        var oBox = document.getElementById("box");
        var oLis = oBox.getElementsByTagName("li");

        for (var i = 0; i < oLis.length; i += 2) {
            oLis[i].onclick = function () {
                this.style.backgroundColor = "red";
            }
        }
    </script>
</body>
</html>

九九乘法算表

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>九九乘法算表</title>
</head>
<body>
<div id="box">

</div>
<script>
    var oBox = document.getElementById("box");
    for (var i = 1; i < 10; i++) {
        // 第一个for控制行的
        for (var j = 1; j <= i; j++) {
            // 第二个for 执行这一行的  签的乘数
            var math = j + "*" + i + "=" + j*i+" ";
            console.log(math)
            oBox.innerHTML = oBox.innerHTML + math;
        }
        // 在每一行执行结束后,添加一个换行
        oBox.innerHTML += "<br>";
    }


</script>
</body>
</html>

流程控制

使用break、label、continue、return语句可以中途改变分支结构、循环结构的流程方向,以提升程序的执行效率。return在函数中会做详细讲解。

label语句

  • 在JS中,label语句可以为一行语句添加标签,以方便在复杂的结构中设置跳转目标。
  • 语法如下:label:语句
  • label可以是任意合法的标识符,然后使用冒号分割标签名与标签语句。
  • label和break语句配合使用,主要应用在循环结构、多分枝结构中,以便与跳出内层嵌套体。
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++;
    }
}
console.log(num);

break语句

  • break语句能够结束当前for、for/in、while、do/while或者switch语句的执行。同时break可以接受一个可选的标签名,来决定跳出的结构语句。
  • 如果没有设置标签名,则跳出当前最内层结构。
//break 是立即退出循环 或 退出switch语句  执行循环后边的代码
var  num =0 ;
for ( var i=0 ; i<5 ; i++ ) {
    if ( i == 3 ) {
        break;
    }
    num++;
}
alert( num );



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++;
    }
}
console.log(num);

continue语句

  • continue语句用在循环结构内,用于跳出本次循环中剩余的代码,并在表达式的值为真的时候,继续执行下一次循环。
  • 可以接受一个可选的标签名,来决定跳出的循环语句。
var  num =0 ;
for ( var i=0 ; i<5 ; i++ ) {
    if ( i == 3 ) {
        continue;
    }
    num++;
}
alert( num );


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++;
    }
}
console.log(num)

异常处理

ECMA-262规范了7种错误类型。其中Error是基类,其他6种错误类型是子类,都继承了基类。Error类型的主要作用是自定义错误对象。

  • Error:普通异常 。与thorw语句和try/catch语句一起使用,属性name可以读写异常类型,message属性可以读写详细的错误信息。
  • EvalError:不正确使用eval()方法时抛出
  • SyntaxError:出现语法错误时抛出
  • RangeError:数字超出合法范围之抛出
  • ReferenceError:读取不存在的变量时抛出
  • TypeError:值的类型发生错误的时候抛出
  • URIError:URI编码和解码错误时抛出

try/catch/finally语句

  • try/catch/finally语句是异常处理语句

    try{

    ​ 调试代码块

    }

    catch(e){

    ​ 捕获异常,并进行异常处理的代码块

    }

    finally{

    ​ 后期清理代码块

    }

  • 正常情况下,JS按顺序执行try子句中的代码,如果没有异常发生,将会忽略catch跳转到finally子句继续执行。

  • 如果在try子句运行错误,或者使用throw语句主动抛出异常,则执行catch子句中的代码,同时传入一个参数,引用Error对象

// try catch 案例
try{
    console.log(a);//当try中出现错误的时候,会直接进入catch执行
    console.log("我是try");
}catch (e) {
    console.log("catch");
    console.log(e);//referenceError:a is not defined
    console.log(e.name);//referenceError
    console.log(e.message);//a is not defined
}


try{
    console.log("try");
    throw new Error("你是不是没有定义变量");
}catch (e) {
    console.log("catch")
    console.log(e);
    console.log(e.name);
    console.log(e.message);
}



try{
    console.log("try");
    // throw new Error("你是不是没有定义变量");
}catch (e) {
    console.log("catch")
    console.log(e);
    console.log(e.name);
    console.log(e.message);
}finally {
    console.log('finally')
}

alert(1);

// throw是抛出错误
var a = 10;
if(a > 4){
    // throw "你错啦吗?";
    // throw new TypeError("你的类型错误");
    var userError = "你今天犯错了,错误如下。。。。。。。。";
    throw new SyntaxError(userError);
}

语句练习

收银程序

输入单价和数量,计算总价。如果总价大于500 则打八折。然后用户输入付钱,最终弹出找零。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>收银程序</title>
</head>
<body>
<script>
    

    //输入商品的单价和数量
    var price = prompt("请输入单价");
    var count = prompt("请输入数量");

    //计算总价
    var sumPrice = price * count;

    //判断是否打折
    if (sumPrice >= 500){
        sumPrice *= 0.8;
    }

    //给用户说需要付多少钱
    var money = prompt("您本次消费"+sumPrice+"元,请付款(输入付钱的面额即可,稍等给您找零)");

    // 判断用户缴费是否足够,并找零
    if (money > sumPrice) {
        // 计算找零
        var reduceMoney = money - sumPrice;
        alert("找零" + reduceMoney + "元,请收好")
    }else{
        alert("钱不够!!!!")
         //此时可以重新递归调用,代码省略
    }

    alert("欢迎下次光临");
</script>
</body>
</html>

ATM按键

输入相应数字,执行相应功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ATM按键</title>
</head>
<body>
<script>
    var userPress = prompt("欢迎光临银行,请输入数字选择功能(1.查询余额,2.取钱,3.转账,4.退出)");
    switch (parseInt(userPress)) {
        case 1:
            search();
            break;
        case 2:
            drag();
            break;
        case 3:
            change();
            break;
        case 4:
            exit();
            break;
        default:
            alert("你丫看不懂提示么");
    }
    
    function search() {
        alert("正在查询余额");
    }
    function drag(){
        alert("正在取钱啊");
    }
    function change() {
        alert("正在转账")
    }
    function exit() {
        alert("正在退出")
    }
</script>
</body>
</html>

买保险

公司给员工买保险(用户通过此程序查询自己是否符合条件):1.只要结婚的都买;2.没有结婚的男人 25岁以下不买;3.没有结婚的姑娘 22岁以下不买.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>买保险</title>
</head>
<body>
<script>
    /*
    * 公司给员工买保险(用户通过此程序查询自己是否符合条件):
    * 只要结婚的都买
    * 没有结婚的男人 25岁以下不买
    * 没有结婚的姑娘 22岁以下不买
     */
    //输入是否结婚
    var isMarry = prompt("你结婚了吗?请输入yes和no");
    var age = prompt("您今年高寿啊");
    var sex = prompt("您性别是什么(man or woman)");
    if (isMarry === "yes"){
        alert("您放心做吧,公司给您提供免费的保险");
    }else if ((sex === "man" && age < 25) || (sex === "woman" && age < 22)){
        alert("自己回家买去吧");
    }else{
        alert("公司也给您买");
    }

</script>
</body>
</html>

函数

  • 函数是一段被封装的代码,可以反复被调用。
  • 在JS中,函数也可以是一个值、一个对像、还可以是一个表达式,因此函数可以赋值、可以运算、可以拥有属性和方法
  • JavaScript拥有函数式编程的很多特性和风格,灵活使用函数,可以编写出功能大、代码简洁、设计优雅的程序。
//自我介绍    
/*  var yourGirlFri = {};
    yourGirlFri.name = "张三";
    yourGirlFri.sex = "女";
    yourGirlFri.age = "40";
    yourGirlFri.price = "388";

    var yourGirlFri = {};
    yourGirlFri.name = "李四";
    yourGirlFri.sex = "女";
    yourGirlFri.age = "18";
    yourGirlFri.price = "188";

    var yourGirlFri = {};
    yourGirlFri.name = "王大麻子";
    yourGirlFri.sex = "女";
    yourGirlFri.age = "12";
    yourGirlFri.price = "1200";
*/

//一段相同的代码重复执行  我们可以书写一个函数把代码包含起来  使用的时候 直接调用函数即可
function yourGirlFri(name,sex,age,height) {
    var obj = {};
    obj.name = name;
    obj.sex = sex;
    obj.age = age;
    obj.height = height;
    return obj;
}

var girl1 = yourGirlFri("lily","男","19","180")
var girl2 = yourGirlFri("王八八","女","100","190")
var girl3 = yourGirlFri("王久久","女","90","110")

console.log(girl1)
console.log(girl2)
console.log(girl3)

定义函数

声明函数

  • 在 Javascript中可以使用function语句声明函数。

    function funName ([args]){

    ​ statements

    }

    • funName是函数名,与变量名一样都必须是JavaScript合法的标识符。
    • 在函数名之后是一个由小括号包含的参数列表,参数之间以逗号分隔。参数是可选的,没有数量限制。
    • 作为标识符,参数仅在函数体内被访问,参数是函数作用域的私有成员。调用函数时,通过为函数传递值,然后使用参数获取外部传入的值,并在函数体内干预函数的运行
    • 在小括号之后是一个大括号,大括号内包含的语句就是函数体结构的主要内容。在函数体中,大括号号是必不可少的,缺少大括号, JavaScript将会抛出语法错误,
  • var语句和function语句都是声明语句,它们声明的变量和函数都在JavaScript预编译时被解析,也被称为变量提升和函数提升。

//直接声明函数,可以直接提升
fn1(1,2,3);
function fn1(a,b,c) {
    console.log(a,b,c)
}


//函数只有定义在了其他函数中,才是局部的函数,否则都是全局的,在哪都能调用
//ES5中,不允许 函数书写在非函数的代码块中
fn2();//oh
console.log(fn2);
var a = 100;
if (a > 10){
    function fn2() {
        alert("oh");
    }
}
fn2();//oh


//函数也可以不书写名字,这种函数被称作为匿名函数
function () {
    alert("heng")
}

//匿名函数的执行方法 1   自调用(分为直接扩住函数体调用、在函数前添加 !~ + - 一元运算符 然后 添加小括号调用)
(function (){
    alert("heng");
})();

!function (){
    alert("heng");
}()

//匿名函数的执行方法 2  赋值调用  (也被称作为函数表达式)  这种方法只能提升fn3  不能提升函数体

var fn3 = function (){
    alert("heng");
}
fn3()

构造函数

  • 使用Function(p1,p2,p3,p4,..pn,body)构造函数可以快速生成函数
  • Functiono的参数类型都是字符串,p1-pn表示所创建函数的参数名称列表,body表示所创建函数的函数结构体语句,在body语句之间以分号分隔。
  • 使用Function()构造函数不是很常用,因为一个函数体通常会包含很多代码,如果将这些代研以一行字符串的形式进行传递,代码的可读性会很差。
var fn1 = new Function("console.log(111)");
fn1();

//以下两种传参方式都可以使用
var fn2 = new Function("a","b","c","console.log(a+b+c)");
var fn2 = new Function("a,b,c","console.log(a+b+c)");
fn2(1,2,3);

函数直接量

  • 函数直接量也称为匿名函数,即函数没有函数名,仅包含函数关键字,参数和函数体,具体用法如下 function (){}
  • 匿名函数就是一个表达式,即函数表达式,而不是函数结构的语句,可以把匿名函数作为值赋值给变量或者对象等等
  • 当把函数结构作为一个值赋值给变量之后,变量就可以作为函数被调用,此时变量就指向那个匿名函数
  • 匿名函数可以自己调用,比如加上小括号然后整体调用,或者在最前边添加!-+~等等一元操作符
// 把函数当做一个值
function fn1() {
    console.log("fn1");
}
var fn2 = fn1;
var fn3 = function (){
    console.log("匿名")
}
fn2();
fn3();

//函数也可以是一个对象
function fn1() {
    alert("fn1");
}
fn1.name1 = "lily";
fn1.sex = "nv";
// fn1();
alert(fn1.name1);
alert(fn1.sex);

//函数可以作为对象的某个方法
var obj = {
    name :"lily",
    sex:"nan",
    say:function () {
        alert("aaaaaaaaa");
    },
    do:fn1
}
console.log(obj.name);
console.log(obj.say());//undefined
obj.do();

调用函数

JavasScipt供4种函数调用模式,函数调用,方法调用,使用call或apply动态调用(后边讲解),使用new间接调用(后边讲解)

函数调用

在默认状态下,函数是不会被执行的,使用小括号可以激活并执行函数。在小括号中可以包含零个或多个参数,参数之间通过逗号进行分隔(可以返回函数并调用)

函数的返回值

  • 函数提供两个接口与外界的交互,其中参数作为入口,接受外界的信息。返回值作为出口,把运算结果反馈给外界。
  • 在函数体内,使用return语句可以设置函数的返回值。一旦执行返回语句,将停止函数的运行,并运算和返回返回后面的表达式的值
  • 如果函数不包含return语句,则执行完函数体内每条语句后,返回undefiend值。
  • JavaScript是一种弱类型语言,所以函数对接收和输出的值都没有类型限制,JavaSeript也不会自动检测输入和输出值的类型
  • 可以返回一个计算值 也可以返回多个值(使用对象或者数组)
 function fn1(a,b){
     var sum = a + b;
     return sum;
     alert("计算完毕");
 }
fn1(1,2);//如果不用变量接受,或者是不使用,那么这个返回值是看不到的
console.log(fn1(1,2)+4);//7


function fn1() {
    var a = 1;
    return function () {
        alert(a);
    }
}
fn1()();
var b = fn1();
console.log(b);
a();


function teacherMessage(name,age,sex) {
    alert("我的名字是"+name);
    alert("我的年龄是"+age);
    alert("我的性别是"+sex);
}

var teacher1 = {
    name:"lily",
    age:"18",
    sex:"nv"
}
var teacher2 = {
    name:"lily1",
    age:"128",
    sex:"nan"
}
function teacherMessage(obj) {
    alert("我的名字是"+obj.name);
    alert("我的年龄是"+obj.age);
    alert("我的性别是"+obj.sex);
}
teacherMessage(teacher1);


function fn1() {
    var a = 1;
    return;//返回的是undefined
    alert(2);
}

console.log(fn1());

方法调用

  • 当一个函数被设置为对象的属性值时,称之为方法,使用点语法可以调用一个方法
var obj = {
    name :"lily",
    sex:"nan",
    say:function () {
        alert("aaaaaaaaa");
    },
    do:fn1
}
console.log(obj.name);
console.log(obj.say());//undefined
obj.do();

函数参数

参数是函数对外练习的唯一入口,用户只能通过参数来控制函数的运行。

01.形参和实参

  • 形参:在定义函数时,声明的参数变量仅在函数内部可见
  • 实参:在调用函数时,实际传入的值
  • 一般情况下,函数的实参和形参的数量应该相同,但是JS并没有这样的要求。可以不相同
  • 如果函数的实参数量少于形参数量,那么多出来的形参会默认会undefined
  • 如果函数实参数量多余形参数量,那么多出来的实参就不能通过形参访问。函数忽略掉多余的实参。
  • 可以设定形参默认值或者判断给值
function f(a,b) {

}
f(1,2);

function f2(a,b,c) {
    console.log(a,b,c);//1 2 undefined
    console.log(typeof c);//undefined
}
f2(1,2);

function f3(a,b,c) {
    console.log(a,b,c)
}
f3(1,2,3,4,5,6,7)

//书写一个函数 计算 三个值的和
function add(a,b,c) {
    return a+b+c;
}

console.log(add(1, 2));//NaN


//直接给函数形参默认值
function add(a,b,c=0) {
    return a+b+c;
}
console.log(add(1, 2));

//判断参数是否存在,否则给出默认值
function add(a,b,c) {
    c = typeof c === "undefined" ? 0 : c;
    typeof c === "undefined" ? c = 0 : c;
    return a+b+c;
}
console.log(add(1, 2));//NaN

获取参数个数

  • 使用arguments对象的length属性可以获取函数的实参个数。
  • argument对象只能在函数内可见,因此arguments.legth也只能在函数体内使用。
  • 使用函数对象的length属性可以获取函数的形参个数,该属性为只读属性,在函数体内,体外都可以使用
function add(a,b,c) {
    alert(add.length)//函数形参的个数
    alert(argument.length)//获取实参的个数
    return a+b+c;
}
alert(add.length)//函数形参的个数
console.log(add(1, 2));

使用arguments对象

  • arguments对象表示函数的实参集合,仅能够在函数体内可见,并可以直接访间。
  • 参数对象是一个伪类数组,不能够继承Array的原型方法。可以使用数组下标的形式访问每个实参,如参数[0]表示第一个实参
  • 通过修改length属性值,可以改变函数的实参个数。
function fn1(a,b,c) {
    console.log(arguments.length);//获取实参的个数
}
fn1(1,2,3,4)

//输入一组数字,求平均值的函数
function f() {
    //先获取到所有的实参 使用arguments
    var sum = 0;
    for (var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
console.log(f(12, 34, 54, 32, 100, 98, 45, 34, 12));


// 直接使用函数对象的length属性,就可以获取到函数的形参个数
function f3(a,b,c) {
    console.log(f3.length);//3
}
f3(1,2,3,4,5,6);


// 检测函数的形参和实参是否一致,如果不一致,则抛出错误
function f4(a,b,c,d) {
    if (arguments.length != f4.length) {
        throw new Error("参数不一致");
    }else{
        alert("go")
    }
}
f4(1,2,3,4,5)

使用callee

callee是arguments对象的属性,它引用当前argument对象所在的函数,使用该属性可以在函数体内调用自身。

// 检测函数的形参和实参是否一致,如果不一致,则抛出错误
function f4(a,b,c,d) {
    if (arguments.length != arguments.callee.length) {
        throw new Error("参数不一致");
    }else{
        alert("go")
    }
}
f4(1,2,3,4,5)

var a = 0;
(function (){
    a++;
    alert(1);
    if (a > 1){
        return;
    }
    arguments.callee()
})();

函数的练习

当点击不同的按钮的时候 让页面加载相应的颜色​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函数的练习</title>
</head>
<body>
<button id="red">红色</button>
<button id="green">绿色</button>
<button id="blue">蓝色</button>
<button id="pink">粉色</button>

<script>
    /*
    * 当点击不同的按钮的时候 让页面加载相应的颜色
    * 改变屏幕的颜色 就是控制body的背景颜色
     */
    var oBody=document.getElementsByTagName("body")[0];
    var oRed = document.getElementById("red");
    var oGreen = document.getElementById("green");
    var oBlue = document.getElementById("blue");
    var oPink = document.getElementById("pink");

    function changeColor(col){
        oBody.style.backgroundColor = col;
    }
    oRed.onclick=function () {
        changeColor("red");
    }
    oGreen.onclick=function () {
        changeColor("green");
    }
    oBlue.onclick=function () {
        changeColor("blue");
    }
    oPink.onclick=function () {
        changeColor("pink");
    }

</script>
</body>
</html>

函数作用域

变量作用域

变量作用域(scope)是指变量在程序中可以访问的有效范围。也称为变量的可见性。分为全局变量和局部变量

  • 全局变量:变量在整个页面中都是可见的,可以被自由的访问
  • 局部变量,变量仅能在声明的函数内部可见,函数外是不允许访问的。
  • var声明的作用与是按照函数划分的
var a = 1;
function f() {
    console.log(a);
}
f();

function f2 (){
    var a = 2;
    function f3() {
        console.log(a);
    }
    f3();
}
// f3();//f3是局部函数 外部使用不了的
f2();
console.log(a);

执行上下文

  • JS引擎并不是一行行的解析和执行代码,而是一段段的去分析和执行,当执行一段代码时,先开始预处理,比如声明提升和函数提升
  • 在执行某段js代码的时候,会进行一个准备工作,这个准备工作用专业的说法 叫“执行上下文”,其实执行上下文也是在内存中开辟的一个空间
  • js可执行的代码分为3种类型, 全局代码 、 函数代码 、eval代码(忽略)
  • 每执行一段代码,都会创建相对应的执行上下文,在脚本中可能存在多个执行上下文
  • 因为有太多的执行上下文, JS创建了一个执行上下文栈(stack) 用来管理执行上下文
  • 当js开始解析程序的时候,最先遇到的全局代码,此时向执行上下文栈中 压入一个全局执行上下文,全局的一定是在整体运行结束以后才被清空
  • 当执行一个函数的时候 会创建一个函数的执行上下文,并压入到执行上下文栈中,只要函数执行完成,会将函数从栈里弹出
function fun3() {
    console.log("fun3");
}
function fun2() {
    fun3();
}
function fun1() {
    fun2();
}
fun1();

/*
      //用伪代码来实现上边代码执行上下文的流程
      //1.js引擎创建了一个执行上下文栈
      var Stack = [];
      //2.向栈中押入 全局执行上下文
      Stack = [globalContext]
      //3.在fun1中创建执行上下文  并压入栈中
      Stack.push(<fun1>Context)
      //4.在fun2中创建执行上下文  并压入栈中
      Stack.push(<fun2>Context)
      //5.在fun3中创建执行上下文  并压入栈中
      Stack.push(<fun3>Context)
      //6.fun3执行完毕
      Stack.pop();
      //7.fun2执行完毕
      Stack.pop();
      //8.fun1执行完毕
      Stack.pop();
      //9整体代码执行结束  全局执行上下文出去
*/

var scope = "hello";
function checkscope() {
    var scope = "world";
    function f() {
        return scope;
    }
    return f();
}
checkscope();
/*
    var Stack = [globalContext];
    Stack.push(<checkscope>Context);
    Stack.push(<f>Context);
    Stack.pop()
    Stack.pop()
*/

变量对象

  • 每个执行上下文 都有三个重要属性:1.变量对象(VO) 2.作用域链 3.this

  • 变量对象是 ECMAScript规范术语。在一个执行上下文中,变量对象才被激活,只有激活的变量对象,其各种属性才能被访问

  • 变量对象是与执行上下文相关的数据作用域,储存了在上下文中定义的变量和函数声明

全局上下文的变量对象
  • window是预定义对象,作为JS全局函数和全局属性的占位符(全局的变量和函数就是window对象属性和方法)
  • 全局执行上下文的变量对象其实就是全局对象window
函数上下文的变量对象
  • 进入执行上下文 不会立马执行代码,只进行分析。此时首先第一步,变量对象包括了函数所有的形参和实参
  • 检查所有声明的函数,由名称和对应值 组成一个变量对象的属性 被创建。如果变量对象已经有相同名字的属性,则完全替换
  • 检查所有的声明的变量,创建键值对儿
  • 变成变量对象的属性,如果变量名和已经声明的形参或函数相同,则变量声明不会干扰已经存在的这类属性
function foo(a) {
    var b = 2;
    function c() {}
    var d = function () {};
    b = 3;
}
foo(1, 2);

//模拟以上代码的变量对象:
var AO = {
    //arguments是保存所有的的实参
    arguments:{
        0:1,
        1:2,
        length:2
    },
    a:1,
    c:function c(){},
    b:undefined,
    d:undefined
}

//练习
function fn1(a) {
    console.log(a); //1
    var a = 2;
    console.log(a); //2
}
fn1(1);

function fn2() {
    console.log(foo); //函数
    function foo() {}
    var foo = 1;
}
fn2();

作用域链

  • 当代码在一个环境中执行时,会创建变量对象的一个作用域链( scope chain),作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。
  • 作用域链的前端,始终都是当前执行的代码所在环境的变量对象。
  • 全局执行环境的变量对象始终都是作用域链中的最后一个对象。
  • 标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)

this和调用对象

什么是this
  • javascript函数的作用域是静态的,但是函数的调用却是动态的,由于函数可以在不同的运行环境内执行,因此 JavaScript在函数体内定义了this关键字,用来获取当前的运行环境。
  • this是一个指针型变量,它动态引用当前的运行环境,具体来说,就是调用函数的对象。
  • this永远指向其所在函数的调用者,如果没有所有者则指向全局对象window
this的指向方式
  1. 默认绑定 :常用的函数调用类型:独立函数调用

    可以把这个规则看作是无法应用其他规则的时候 默认的规则,基本指向的是window

    function foo() {
        console.log(this);
    }
    foo(); //window
    
    var obj = {
        do: function () {
            foo(); //foo是直接使用不带任何修饰的函数引用进行调用,因此只能使用默认绑定 规则
        },
    };
    obj.do();
  2. 隐式绑定

    当函数引用有上下文对象的时候(obj),隐式绑定规则会把函数中的this绑定到这个上下文对象上

    function foo() {
        console.log(this.a);
    }
    var obj = {
        a: 2,
        foo: foo, //
    };
    obj.foo(); // //当foo调用的时候,它的落脚点确实是指向的obj对象,当函数引用有上下文对象的时候(obj),隐式绑定规则会把函数中的this绑定到这个上下文对象上
  3. 隐式绑定可能会出现隐式丢失的问题 :被隐式绑定的函数,会丢失了绑定对象

    function foo() {
        console.log(this.a);
    }
    var obj = {
        a: 2,
        foo: foo,
    };
    var fn1 = obj.foo;
    var a = "hello";
    fn1(); //hello 虽然fn1是obj.foo的一个引用,但是实际上它的引用是foo函数本身,因此fn1其实是一个不带任何修饰的函数调用,属于默认绑定
    
    //
    function foo() {
        console.log(this.a);
    }
    function doFoo(fn) {
        fn(); //传参也是隐式赋值,所以传递函数也是隐式赋值,赋值的是foo函数本身
    }
    var obj = {
        a: 2,
        foo: foo,
    };
    doFoo(obj.foo);
  4. 显式绑定

    function foo() {
        console.log(this.a);
    }
    var obj = {
        a: 2,
    };
    foo.call(obj); //2 //通过call 调用foo时,把foo的this强制的绑定给了obj上
  5. new绑定

    构造函数只是一些使用new操作符被调用的函数,使用new调用函数的时候,会构造一新的对象,这个时候 就把新的对象绑定给了函数的this上

    function Foo(a) {
        this.a = a;
    }
    var bar = new Foo(2);
    console.log(bar.a); //2
怎么判断this指向
  1. 函数是否在new中调用,如果是的话,this绑定的是新创建的对象
  2. 函数是否通过call、apply(显示绑定)调用,如果是,则this绑定的是执行的对象
  3. 函数是否在某个上下文对象中调用(隐式绑定),如果有,则this绑定在这个上下文对象上
  4. 如果以上都不是 则默认绑定 执行window
//obj.f在上面代码中,obj.f表示在obj对象上调用f函数,则调用对象为obj,此时this就指向obj,this.x就等于obj.x,即返回结果为2
//若把obj.f赋值给变量f1,然后在全局上下文中调用们函数,则f函数体的运行环境在全局上下文中执行,此时this就指向 window, this.x就等于 window.x,即返回结果为1
var x = 1;
var obj = {
    f:function(){
        console.log(this.x)
    },
    x:2
}
obj.f();
var f1 = obj.f;
f1();
其他练习
var x = 1;
var obj = {
    f:function(){
        console.log(this.x);
    }
    x:2,
}
  
(obj.f = obj.f)();//正确理解“运行环境”obj.f=obj.f是赋值表达式,把obj.f赋值给obj.f,obj.f是一个地址,把地址赋值给obj.f属性,表达式的运行环境发生在全局上下文中,所以此时函数f内的this就指向了全局上下文的调用对象 window
(false||obj.f)();//逻辑表达式,左侧操作数为false,则运算右侧操作数,返回obj.f的值,即引用地址。由于这个逻辑表达式运算发生在全局作用域内,此时的f函数内this就指向了全局对象
(obj.f,obj.f);//逗号运算表达式,逗号左侧和右侧的obj.f都是一个地址,都被运算一次,最后返回第2个操作数的值,即返回引用地址。由于这个操作发生在全局作用域内,所以f函数内this也指向了全局对象
(obj.f)//his指向obj对象,因为小括号不是一个运算符,它仅是一个逻辑分隔符,不执行运算,不会产生运行环境,当使用小括号调用函数时,此时生成的运行环境就是obj

作用域练习

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
    //demo1
    var v = "hello";
    (function(){
        alert(v);
        var v = "world";
    })();
    alert(v);

    //demo2
    (function(){
        alert(a+b+c);
        var a = "1";
        var f = function(){};
        var b = "2";
        var c = "3";
    })();

    //demo3
    (function(){
        f2();
        f1();
        var f1 = function(){
            alert(2)
        };
        function f2(){
            alert(1);
        }
    })();

    //demo4
    var a = 1;
    function outer(){
        var b = 2;
        function inner(){
            var c = 4;
            alert(a);
        }
        inner();
    }
    outer();

    //demo5
    var a = 1;
    function check(){
        a = 100;
        alert(a);
    }
    check();
    alert(a);

    //demo6
    var v = "hello";
    if(true){
        alert(v);
        var v = "world";
    }
    alert(v);

    //demo7
    function rain(){
        var x = 1;
        function man(){
            x = 100;
        }
        man();
        alert( x );
    }
    rain();

    //demo8
    var x = 1;
    function rain(){
        alert( x );
        var x = 'man';
        alert( x );
    }
    rain()


    //demo9
    function rain(){
        x = 100;
    }
    rain();
    alert( x );//100

    //demo10
    var t='a';
    function test2(){
        alert(t);
        var t='b';
        alert(t);
    }
    test2();

    //demo11
    var t='a';
    function test2(){
        alert(t);
        t='b';
        alert(t);
    }
    alert(t);
    test2();
    alert(t);

    //demo12
    var a="Hello";
    function test(){
        alert(a);
        a="World";
        alert(a);
    }
    test();

    //demo13
    var a =1;
    function test(){
        alert(a);
        a=4;
        alert(a)
        var a=2;
        alert(a);
    }
    test();
    alert(a);

    </script>
</body>
</html>

JS对象

什么是对象

  • js中,任何值都可以转换为对象,以对象的方式进行使用,如数字对象、布尔值对象、字符串对象、类型对象、函数对象、数组对象等,它们都继承 Object类型对象,拥有共同的基本属性和方法。
  • 此外, JavaScript也允许自定义对象,从狭义的概念来分析,对象(Object)是最基本的数据类型,是复合型的结构、引用型的数据,它是无序数据集合,对象中每个成员被称为属性。

定义对象

构造函数

  • 使用new运算符调用构造函数,可以构造一个实例对象,具体用法如下var objectName = new functionName (args)
  • 参数说明如下
    • objectName:返回的实例对象
    • functionName:构造函数,与普通函数基本相同
    • args:实例对象初始化配置参数列表
var num1 = new Number(111);
console.log(num1);

var obj = new Object({
    name:1
});
console.log(obj);

对象直接量

  • 使用直接量可以快速定义对象,也是最高效、最简便的方法。var objectName = {属性名1:属性值1,属性名n:属性值}
  • 在对象直接量中,属性名与属性值之间通过冒号进行分隔,属性值可以是任意类型的数据,属性名可以是 JavaScript标识符,或者是字符串型表达式,属性与属性之间通过逗号进行分隔,最后一个属性末尾不需要逗号。
  • 如果属性值是对象,可以设计嵌套结构的对象
  • 如果不包含任何属性,则可以定义一个空对象
var obj1 = {
    name:"lily",
    "sex":"女",
    score:[100,90,80,12,35,34],
    fri:{
        fir1:"xiaowang",
        fir2:"xiaoli",
        fir3:{
            sis1:"dahua",
            sis2:"xiaohua",
            sis3:["zhangdama","zhangxiaoma","zhangma"]
        }
    }
}
console.log(obj1.sex);

使用 Object create

  • Object.create是 ECMAScript5新增的一个静态方法,用来定义一个实例对象。
  • 该方法可以指定对象的原型和对象特性。具体用法如下object.create (prototype, descriptors)
    • prototype:必须参数,指定原型对象,可以为null
    • descriptors可选参数,包含一个或多个属性描述符的 JavaScript对象。属性描述符包含数据特性和访问器特性,其中数据特性说明如下
      • value:指定属性值
      • writable:默认为 false,设置属性值是否可写
      • enumerable:默认为 false,设置属性是否可枚举( for/in)
      • onfigurable:默认为flse,设置是否可修改属性特性和删除属性
      • 访问器特性包含两个方法,简单说明如下set():设置属性值,get():返回属性值
//Object.create方法
var obj = {};
console.log(obj);

var obj2 = Object.create(obj1);
console.log(obj2);//空对象  但是继承了obj1
console.log(obj2.name);//自己没有没关系  他爹有

//创建一个干净的对象
var obj3 = Object.create(null);
console.log(obj3);

//创建一个对象
var obj4 = Object.create(null,{
    name:{
        value:"xiaowang",
        writable:true,
        enumerable:true,
    },
    sex:{
        value:"nv"
    }
})
console.log(obj4);
console.log(obj4.name);
obj4.name = "laowang";
console.log(obj4);

for(i in obj4){
    console.log(i);
}


//访问器属性
var obj5 = Object.create(null,{
    a:{
        value:"hello",
        writable:true
    },
    b:{
        get:function () {
            return this.a+" world"
        },
        set:function (i) {
            this.a = i + "我拖堂了";
        }
    }
})
console.log(obj5);
console.log(obj5.b);//当调用一个属性的时候,会访问他的访问器属性的get,get方法的返回值就是b的值
obj5.b = "hahaha";//当设置属性的时候,会调用访问器的set方法,设置的值就是方法的参数
console.log(obj5);

对象属性的操作

属性也称为名值对,包括属性名和属性值。一个对象中不能存在两个同名的属性。属性值可以是任意类型的数据

定义属性

直接量定义

在对象直接量中,属性名与属性值之间通过冒号分隔,冒号左侧是属性名,右侧是属性值,名值对(属性)之间通过逗号分隔。

//使用直接量定义
var obj1 = {
    name:"laowang",
    sex:"男"
};
var obj2 = new Object({
    name:"xiaowang",
    sex:"nv"
})
var obj3 = Object.create(null,{
    name:{
        value:"dawang",
        writable:true
    },
    sex:{
        value:"nan"
    }
})
点语法定义
//点语法定义:
var obj4 = {
    name:"xiaozhang"
}
obj4.sex = "nan";
console.log(obj4);

var obj5 = new Object({
    name:"xiaowang",
    sex:"nv"
})
obj5.age = 20;
console.log(obj5);

var obj6 = Object.create(null,{
    name:{
        value:"dazhang",
        writable:true,
    }
})
obj6.age = 10;//也可以直接对 create创建的对象设置属性,但是无法选择属性的特性,默认可以被修改,可以被枚举
console.log(obj6);
obj6.age = 20;
console.log(obj6);
for(var i in obj6){
    console.log(i);
}
中括号语法
var obj7 = {
    name:"xiaowang"
}
var  a= "sex"
obj7[a] = "nv";
obj7["sex"] = "nv";
console.log(obj7);


function getMess(obj,pro) {
    return obj[pro]
}
var myself = {name:"huahua",sex:"nan",age:"19"};
console.log(getMess(myself, "sex"));
Object.defineProperty

使用 Object.defineProperty函数可以为对象添加属性,或者修改现有属性。如果指定的属性名在对象中不存在,则执行添加操作:如果在对象中存在同名属性,则执行修改操作

// Object.defineProperty(obj,pro,{})
var obj8 = {
    name:"xiaoli"
}
Object.defineProperty(obj8,"sex",{
    value:"nv",
});
Object.defineProperty(obj8,"name",{
    //如果修改原有的name属性值,它可以被修改和枚举
    value:"dali",
});
console.log(obj8);
for(var i in obj8){
    console.log(i);
}
使用Object.defineProperties
  • 可以一次定义多个属性
  • Object.defineProperties(object,description)
    • object:对其添加或修改属性的对象,可以是本地对象或DOM对象
    • description:包含一个或多个描述符对象,每个描述符对象描述一个数据属性或访问器属性
var obj9 = {
    like:"miantiao"
}
Object.defineProperties(obj9,{
    color:{
        value:"yellow",
        enumerable:true
    },
    length:{
        value:"10m",
    }
})
console.log(obj9);

读属性

使用点语法

使用点语法可以快速读写对象属性,点语法左侧是引用对象的变量,右侧是属性名。

var obj1 = {
    name:"xiaowang",
    like:"唱跳rap篮球",
    time:"两年半"
}
console.log(obj1.name)
使用中括号语法
  • 从结构上分析,对象与数组相似,因此可以使用中括号来读写对象属性
  • 在中括号语法中,必须以字符串形式指定属性名,不能使用标识符。
  • 中括号内可以使用字符串,也可以使用字符型表达式,即只要表达式的值为字符串即可
// 案例 for in 遍历重写
var obj1 = {
    name:"xiaowang",
    like:"唱跳rap篮球",
    time:"两年半"
}

for(var item in obj1){
    console.log(item);
    obj1[item] = obj1[item] + "@";
}
console.log(obj1);
Object.getOwnPropertyNames
  • 使用 Object.getOwnPropertyNames函数能够返回指定对象私有属性的名称。
  • 私有属性是指用户在本地定义的属性,而不是继承的原型属性。
var obj1 = {
    name:"xiaowang",
    like:"唱跳rap篮球",
    time:"两年半"
}
console.log(Object.getOwnPropertyNames(obj1));//["name","like","time"]
使用Object.keys

使用 Object.keys()函数仅能获取可枚举的私有属性名称,返回值是一个数组,其中包含对象的可枚举属性名称

Object.getOwnPropertyDescriptor()
  • 能够获取对象属性的描述符
  • Object.getOwnPropertyDescriptor(object,propertyname)
  • 参数object表示指定的对象,propertyname表示属性的名称,返回值为属性的描述符对象
//对 create方法创建的对象 进行属性设置
var obj2 = Object.create(null,{
    name:{
        value:"新宝岛",
        enumerable:true
    },
    time:{
        value:"5min",
        enumerable:true
    },
    like:{
        value:"rap"
    }

})
console.log(Object.getOwnPropertyNames(obj2));//["name","like","time"]
console.log(Object.keys(obj2));//["name","time"]

console.log(Object.getOwnPropertyDescriptor(obj1, "like"));//{value: "唱跳rap篮球@", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj2, "time"));//{value: "5min", writable: false, enumerable: true, configurable: false}

删除属性

使用 delete运算符可以删除对象的属性 当删除对象属性之后,不是将该属性值设置为 undefined,而是从对象中彻底清除属性

var obj = {
    name:"laowang",
    sex:"nan",
    like:undefined
}
console.log(obj.like);//可以设置一个属性值为undefined
console.log(Object.getOwnPropertyNames(obj));//设置为undefined值的属性名 仍然可以获取到

delete obj.sex;//删除一个属性
console.log(obj);
console.log(Object.getOwnPropertyNames(obj));//当一个属性被删除的话,就枚举不到他的这个属性名了

控制对象状态

  • Object.preventExtensions():阻止为对象添加新的属性
  • Object.seal():阻止为对象添加新的属性,同时也无法删除旧属性。等价于把属性描述对象的configurable属性设为false。注意,该方法不影响修改某个属性的值
  • Object.freeze():阻止为一个对象添加新属性、删除旧属性、修改属性值 同时提供3个对应的辅助检查函数
  • Object.isExtensible():检查一个对象是否允许添加新的属性
  • Object.isSealed():检查一个对象是否使用了 Object.seal方法
  • Object.isFrozen():检查一个对象是否使用了 Object.freeze方法
// 1.Object.preventExtensions():
var obj = {
    name:"xiaoxinxin",
    width:"200",
    height:"300"
}
console.log(obj);
obj.color = "red";//设置新属性
console.log(obj);
console.log(Object.isExtensible(obj));
Object.preventExtensions(obj);//阻止设置新属性
console.log(Object.isExtensible(obj));
obj.bg = "green";
console.log(obj);//阻止以后 无法设置新属性


// 2.Object.seal():
var obj2 = {
    name:"dadudu",
    width:"200",
    height:"300"
}
console.log(obj2);
delete obj2.width;//删除一个旧属性
console.log(obj2);
console.log(Object.isSealed(obj2));
Object.seal(obj2)//阻止添加新属性 和删除旧属性
console.log(Object.isSealed(obj2));
delete obj2.height;
console.log(obj2);


// 3.Object.freeze()
var obj3 = {
    name:"xiaowangba",
    width:"200",
    height:"300"
}
console.log(obj3);
obj3.width = "1000";//修改旧属性
console.log(obj3);
console.log(Object.isFrozen(obj3));
Object.freeze(obj3);
console.log(Object.isFrozen(obj3));
obj3.height = "800";
console.log(obj3);

对象的遍历

  • for in 循环 专门用来遍历对象
  • for in中定义的变量 代表着 对象的键名
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对象的遍历</title>
</head>
<body>
<script>
    var person = {
        headTeacher:{
            name:"小宁姐",
            sex:"20",
            age:"nv",
            method:"班主任"
        },
        jobTeacher:{
            name:"丽丽",
            sex:"22",
            age:"nv",
            method:"就业"
        },
        bossTeacher:{
            name:"老谭",
            sex:"50",
            age:"男",
            method:"校长"
        }
    }
    console.log(person.length);//undefined 对象是没有length属性的

    function teacherMessage(obj) {
        alert("我的名字是"+obj.name);
        alert("我的年龄是"+obj.age);
        alert("我的性别是"+obj.sex);
        alert("我的作用是"+obj.method);
    }

    // for in 循环  专门用来遍历对象
    // for in中定义的变量 代表着 对象的键名
    for(var i in person){
        // console.log(i);
        teacherMessage(person[i]);
    }
</script>
</body>
</html>

基本类型和引用类型

概念

  • 数据类型中 分为5种简单的数据类型和1种复杂的数据类型,他们分别对应着基本类型值和引用类型值
  • 基本类型值:null undefined string number boolean
  • 引用类型值:object(正则、数组、对象、函数。。。。。)
  • 什么这么分类:是按照这两种值存储的方式不同来分类的

基本类型值

  • 基本类型值:null undefined string number boolean
  • 基本类型值都是按值访问 直接操作保存在变量中的实际值
  • 基本类型值都是存储在栈区中,我们可以直接通过变量名访问实际值
var num1 = 10;//在栈区开启一个空间  栈区的名是变量名  栈区的值是 这个变量所对应的的值
var num2 = num1; //重新开辟一个区域保存num2的值,获取num1的值 并赋值给num2
num1 = 30;//改变栈区num1的值为30   但是不会影响num2
console.log(num1,num2);//30 10
  • 基本类型值的特点:
    1. 基本类型的值是不可变的
    2. 我们不能给基本类型值添加属性和方法 就算添加也是获取不到的
    3. 基本类型的比较是值的比较
    4. 基本类型的变量是存放在栈区的(栈区指内存里的栈内存),栈区包括了变量的标识符和变量的值
// 1、基本类型的值是不可变的
/*数组添加一个值,还是原来的数组。
  如果基本类型值发生改变,那么久不再是原来的值,而是重新赋值了。
  所以我们不能改变基本类型的值,否则就是直接换了值
*/

// 2、我们不能给基本类型值添加属性和方法  就算添加也是获取不到的
var str1 = "abc";
str1.eat = "apple";//给基本类型 扩展了一个属性  eat
str1.say = function () {//给基本类型值 扩展一个方法
    alert("你说话呀");
}
console.log(str1.eat);//undefined
str1.say();//str1.say is not a function


// 3、基本类型的比较是值的比较
var num1 = 5;
var num2 = null;
console.log(num1 < num2);//基本类型值的比较 都是拿出变量所对应的值来进行比较的  先取出来再转换类型比较

// 4、基本类型的变量是存放在栈区的(栈区指内存里的栈内存),栈区包括了变量的标识符和变量的值

引用类型值

  • 引用类型值再栈区储存的是 标识符(变量名)和引用地址 在堆区储存的是对象的值
  • 当我们访问某一个对象的时候,要先访问到栈区的地址 然后引用到堆区的值
var obj1 = {
    name:"lily"
}//声明一个对象,然后值保存在堆区  变量和引用地址保存在栈区
var obj2 = obj1;//赋值都是 栈区引用地址的赋值  把obj1的地址赋值给了obj2
obj1.name = "lucy";//改变了obj1 堆区对象值的内容
console.log(obj1,obj2);//obj1 和obj2 引用地址是一样的 所以指向的是同一个对象
  • 引用类型值特点:
    1. 引用类型的值是可变的,我们可以为引用类型添加属性和方法
    2. 引用类型的值是同时保存在栈内存和堆内存中的对象
    3. 引用类型的比较是引用的比较
// 1、引用类型的值是可变的,我们可以为引用类型添加属性和方法
var arr1 = [1,2,3];
arr1.say = function () {
    alert("你好呀");
}
arr1.say();

// 2、引用类型的值是同时保存在栈内存和堆内存中的对象

//3、引用类型的比较是引用的比较
var obj1 = {};
var obj2 = {};
console.log(({} == {}));//false  两个对象虽然是空对象  然后再栈区的地址不一样  而比较是栈区的引用地址的比较  所以返回false

JS数组

什么是数组

  • 数组(Array)是有序数据集合
  • 数组中的每个成员被称为元素(Element),每个元素的名称(键)被称为数组下标(index)
  • 数组内不同元素的值可以为不同类型
  • 数组的长度是弹性的、可读写的
  • 在Javascript脚本中数组主要用于临时寄存同类数据,进行高速批量运算。
var arr = [123,"abc",null,undefined,{name:"xioawang"},true,[1,2,3],function () {
    alert(1);
}];
console.log(arr);
// arr[arr.length - 1]();
arr.length = 100;
console.log(arr);

定义数组和读写数组

构造数组

  • 使用new运算符调用Array()类型函数时,可以构造一个新数组
  • 直接调用Array函数,不传递参数,可以创建一个空数组
  • 传递多个值,可以创建一个实数组
  • 传递一个数值参数,可以定义数组的长度,即包含元素的个数,参数值等于数组的length属性值,每个元素的值默认值为 undefined
  • 使用中括号可以读写数组,中括号左侧是数组名称,中括号内为数组下标
// 1.构造数组:
var arr1 = new Array();//如果不传值,则构造一个空数组
console.log(arr1);

var arr2 = new Array("a","b",23,45);
console.log(arr2);

var arr3 = new Array(4);
console.log(arr3);//如果传递一个参数,是定义数组的长度

var arr4 = new Array("a");
console.log(arr4);

console.log(arr2[2]);//读取数组的值

数组直接量

数组直接量的语法格式:在中括号中包含多个值列表,值之间用号分隔(推荐使用数组直接量定文d 数组,因为数组直接量是定义数组最简便、最高效的方法)

    var arr5 = [1,2,3,4,5];//直接量定义数组
    console.log(arr5);
    console.log(arr5[3]);//读取数组

多维数组

  • Javascript不支持多维数组,设置元素的值等于数组,可以模拟二维数组结构,如果三维组中每个元素的值也为数组,则可以模拟三维数组,以此类推,通过数组套的形式可以定义多推数组
  • 读写多维数组方法与普通方法相同,都是使用中括号进行访问
var arr6 = [1,2,3,[4,5,6],7];//定义一个二维数组
console.log(arr6);
console.log(arr6[3][1]);//二维数组的获取值
var arr7 = [1,2,3,4,5,[5,6,7,[8,9],11],10];//定义一个三维数组
console.log(arr7);
console.log(arr7[5][3][1]);//三维数组的获取



//使用for 把1-100的整数以二维的形式储存在数组中
var arr8 = [];
// 遍历10次,每次生成一个10个值的数组
for (var i = 0; i < 10; i++) {
    // 创建一个新数组,每次遍历的时候,插入10个值,然后把这个数组,插入到arr8中
    var arr9 = [];
    //遍历10次,每次向arr9中插入1个值
    for (var j = 0; j < 10; j++) {
        arr9[j] = i * 10 + j + 1;
    }
    arr8[i] = arr9;
}
console.log(arr8);

空位数组

  • 空位数组就是数组中包含空元素。所谓空元素,就是在语法上数组中两个逗号之间没有任何值。出现空位数组的情况如下
    • 直接量定义
    • 构造函数定义
    • delete删除
  • 空元素可以读写, length属性不排斥空位,如果使用for语句和 length属性遍历数组,空元素都可以 被读取,空元素返回值为undefined
var arr10 = [1,2,,4];//空位数组,找不到,所以空位的值返回undefined
var arr11 = [1,2,undefined,4];//有值,值为undefined
console.log(arr10);
console.log(arr11);

关联数组

  • 如果数组的下标值超出范围,如负数、浮点数、布尔值、对象或其他值,js会自动把它转换为一个字符串,并定义为关联数组。
  • 关联数组就是与数组关联的对象,简单地说就是数组对象,字符串下标就是数组对象的属性
    var arr12 = [1,2];
    arr12[3.1] = "haha";//相当于给数组扩展了一些属性和方法
    arr12[true] = "buer";
    console.log(arr12);
    console.log(arr12[3.1]);

伪类数组

  • 伪类数组,也称为类数组,即类似数组结构的对象。
  • 简单地说,就是对象的属性名为非负整数,且从0开始,有序递增,同时包含length属性,还应确保其值与有序下标属性个数保持动态一致,以方便对伪类数组进行选代操作。
  • 由于数字数非法标识符,所以需要用中括号来读写属性
    var obj1 = {
      0:"a",
      1:"2",
      2:"c",
      3:"d",
      length:4
    }
    console.log(obj1[1]);

斐波那契数列

//打印出斐波那契数列的前100个值 在数组中
// 1,2,3,5,8,13 初始值1和2   其他的值是前两个值的和
// arr[6] = arr[4]+arr[5]
var arr = [1,2];
for (var i = 2; i < 100; i++) {
    arr[i] = arr[i-2] + arr[i-1];
}
console.log(arr);

数组的长度

  • 每个数组都有一个length属性,该属性返回数组的最大长度。
  • length属性可读可写,是一个动态属性。
  • length属性值也会随数组元素的变化而自动更新。同时如果重置了length属性值,也将影响数组的元素。
var arr1 = [];
arr1.length = 100;
console.log(arr1);//empty*100
console.log(arr1[10]);//undefined

var arr2 = [1,2,3];
arr2.length = 5;
console.log(arr2);

var arr3 = [1,2,3,4,5,6,7];
arr3.length = 2;
console.log(arr3);//[1,2]

操作数组

栈操作

  • 使用push()和pop()方法,可以在数组的尾部执行操作。
  • 其中push方法,能够把一个或多个参数附加到数组的尾部,并返回附加元素以后,数组的长度
  • pop方法是能够删除数组中的最后一个元素,并返回删除的值
  • 补充:栈是先进后出,后进先出的原则。类似的情况是,叠放物品,或者子弹上膛
var arr = [];//空栈
console.log(arr.push(1));//入栈 返回新长度
console.log(arr.push(2,3));//入栈 一次可以插入多个值 返回新长度
console.log(arr);

console.log(arr.pop());//出栈 返回删除的值
console.log(arr.pop());//出栈 返回删除的值
console.log(arr);
  • 栈操作练习
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>栈操作练习 -进制转换</title>
</head>
<body>
<script>
    function to2b(num) {
        var arr = [];//一个新栈,把每次生成的余数压到栈里
        var r = null;//用来保存每次生成的余数
        var s = "";//最后返回的二进制字符串
        while(num > 0){
            r = num % 2;//求出每次的余数
            arr.push(r);//把余数入栈
            num = parseInt(num / 2);//求出num计算后的值 并向下取整
        }
        while(arr.length > 0){
            s += arr.pop();//pop是把最后一个值删了 并返回出来 和s拼接  放在了s的首位
        }
        return s;
    }

    console.log(to2b(100));
    console.log(to2b(1000));
    console.log(to2b(3));
    // console.log((100).toString(2));//1100100
</script>
</body>
</html>
  • 栈数据结构方法封装
var arr = [3,5,2,3,6,1,6];
// push:添加一个新元素到栈顶
function push(arr) {
    // arguments
    for (var i = 1; i < arguments.length; i++) {
        arr.push(arguments[i]);
    }
}
push(arr,1,2,3)

// pop方法 移出栈顶的元素  并且得到移出的值
function pop(arr) {
    return arr.pop();
}

// peek:返回栈顶元素 不对栈做修改
function peek(arr) {
    return arr[arr.length - 1]
}

// console.log(peek(arr));

// isEmpty 判断栈中是否为空  如果为空则返回true 否则返回false
function isEmpty(arr) {
    return arr.length == 0
}

// clear:清空整个栈
function clear(arr) {
    arr.length = 0;
}

// size:返回栈里的个数
function size(arr) {
    return arr.length;
}
  • 栈结构练习-可定制的进制转换
//封装一个栈操作的一个类  想要使用 直接new 实例化
//实例化得到的对象 就会拥有这些方法
function Stack() {
    this.arr = [];
    this.push = function (item) {
        this.arr.push(item);
    };
    this.pop = function () {
        return this.arr.pop();
    };
    this.peek = function () {
        return this.arr[this.arr.length - 1];
    };
    this.isEmpty =function () {
        return this.arr.length == 0;
    };
    this.clear = function () {
        this.arr = [];
    };
    this.size = function () {
        return this.arr.length;
    };
}

// 16进制以下所有进制转换算法
function divideBy2(num,para) {
    //把Stack实例化以后  所有的remStack就会拥有Stack这个类的属性和方法
    var remStack = new Stack();
    var rem;
    var binarySting = "";
    var str = "0123456789abcdef";
    //循环插入栈
    while(num>0){
        rem = parseInt(num % para);
        remStack.push(rem);
        num = parseInt(num / para);
    }
    // 当全部插入完成以后,对栈进行一个移出的操作
    while(!remStack.isEmpty()){

        binarySting += str[remStack.pop()];
    }
    return binarySting;
}

console.log(divideBy2(2821355,12));
console.log(divideBy2(4,16));

队列操作

  • 使用unshift()和 shift()方法可以在数组头部执行操作
  • 其中unshift()能够把一个或多个参数值附加到数组的头部,第1个参数为数组新的元素0,第2个参数为新的元素1,以此类推,最后返回添加元素后的数组长度
  • shift方法能够删除数组第1s个元素,并返回该元素,然后将余下所有元素前移1位,以填补数组头部的空缺。如果数组为空, shift将不进行任何操作,返回 undefined
  • 使用 unshift分批插入元素与一次性插入元素结果是不同的
  • 将pop()与 unshift()方法结合,或者将 push()与 shift()方法结合使用,可以模拟队列
// 利用队列模式把数组元素的所有值放大10倍
var a = [1,2,3,4,5];
for(var i in a){
    var t = a.pop();
    a.unshift(t*10);
}
console.log(a);
  • 补充:队列也是一种运算受限的线性表,不过与栈操作不同,队列只允许在一端进行插入操作,在另一端进行删除操作。
  • 队列通循先进先出、后进后出的原则,类似的行为在生活中比较常见,如排队购物、任务排序等。在 Javascript动画设计中,也会用到队列操作来设计回调函数。
  • 击鼓传花练习
// 猴子找大王游戏
// 有一群猴子排成一圈,按1、2、3、…、n依次编号,然后从第1只开始数,数到第m只,则把它踢出圈,然后从它后面再开始数,当再次数到第m只,继续把它踢出去,以此类推,直到只剩下一只猴子为止,那只猴子就叫作大王。要求编程模拟此过程,输入m和n,输出最后那个大王的编号
// n表示猴子个数,m表示踢出位置
function f(n,m){
    //将猴子编号并放入数组
    var arr = [];
    for(var i = 1;i < n+1;i++){
        arr.push(i);
    }
    //当数组内只剩下一只猴子的时候跳出循环
    while(arr.lenght>1){
        //定义排队轮转次数
        for(var i = 0;i<m-1;i++){
            //队列操作完成轮转
            arr.push(arr.shisft());
        }
        arr.shift();//提出第m只猴子
    }
    return arr;
}
  • 队列结构的封装
/*  enqueue:向队列的尾部添加新的项
    dequeue:移除队列的第一个项,并返回被移除的元素
    front:返回队列的第一个元素,队列不做任何的改动
    isEmpty:如果队列中不包含任何元素,返回true,则返回true,否则返回false
    size:返回队列包含的元素个数
*/

function Queue() {
    this.arr = [];
    this.enqueue = function (item) {
        this.arr.push(item)
    }
    this.dequeue = function () {
        this.arr.shift();
    }
    this.front = function () {
        return this.arr[0];
    }
    this.isEmpty = function () {
        // return this.arr.length == 0;
        return !this.arr.length;
    }
    this.size = function () {
        return this.arr.length;
    }
}

var queue1 = new Queue();

删除元素

  • 使用pop方法可以删除尾部的元素,使用shift方法可以删除头部的元素
  • 使用delete运算符能删除指定下标位置的数组元素,删除后的元素为空位元素,删除数组length保持不变
  • 使用length属性可以删除尾部一个或多个元素,甚至可以清空整个数组
  • 使用splice方法可以删除指定下标位置后一个或多个数组元素(个数)
// 使用delete删除指定下标的元素
var arr1 = [1,2,3,4,5];
delete arr1[2];//删除指定下标的值,当前的值为空,所以数组变成了空位数组 长度不变
console.log(arr1);

// 使用length属性也可以删除数组
var arr2 = [1,2,3,4];
arr2.length = 1;
console.log(arr2);


var arr3  = [1,2,3,4,5,5,6,7,8,8];
console.log(arr3.splice(3));//一个参数代表删到末尾
console.log(arr3);//[1,2,3]

var arr4  = [1,2,3,4,5,5,6,7,8,8];
console.log(arr4.splice(3,3));//两个个参数代表删几个 [4,5,5]
console.log(arr4);//[1,2,3,6,7,8,8]

添加元素

  • 使用 push方法可以在尾部添加一个或多个元素,使用 unshift方法可以在头部附加一个或多个元素
  • 通过中括号和下标值,可以为数组指定下标位置添加新元法来添加元素
  • concat方法能够把传递的所有参数按顺序添加到数组的尾部
    • concat方法可以跟随多个参数,并把它们作为元素按顺序连接到数组的尾部
    • 如果参数是数组,则concat方法会把它打散,分别作为单独的元素连接到数组的尾部
    • 不过concat方法仅能够打散一维数组,它不会打散二维的数组。
    • concat方法将创建并返回一个新数组,而不是在原来数组基础上添加新元素。所以,如果要在原数组着础上添加元素,建议使用 push方法和unshift方法来实现。但是 push方法和 unshift方法不能够打散参数数组,而是把它作为单独的参数执行添加操作
  • 使用 splice方法在指定下标位置后添加一个或多个元素
// 可以使用中括号和下标值 来添加元素,或者是修改元素
var arr1 = [1,2,3,4,5];
arr1[5] = 6;
arr1[10] = 10;//如果给后边元素设置值,中间的空白就变成了空位元素
arr1[1] = "a";//这种方法只能给空位元素设置,或者是其他的没有的位置设置,否则就修改了元素
console.log(arr1);

// concat方法
var arr2 = ["a","b","c"];
console.log(arr2.concat(1, 2, 3));;
console.log(arr2);//原数组不改变

var arr2 = ["a","b","c"];
arr2.push([1,2,3]);//push插入会把数组直接当成1个值插入
console.log(arr2);

var arr2 = ["a","b","c"];
console.log(arr2.concat([1, 2, 3,[4,4,4]]));

// splice方法可以在指定位置的下标添加元素
var arr3 = ["a","b","c","d","e","f"];
arr3.splice(2,0,"g","h");//无论是否删除,都会把第三个参数以后的所有参数 按顺序放在第一个参数下标标记的位置
console.log(arr3);

使用 splice()方法

  • splice()方法的参数都是可选的。如果不给它传递参数,则该方法不执行任何操作
  • 如果给它传递一个数,则该方法仅执行删除操作,参数值指定删除元素的起始下标(包括该下标元素),splice方法将删除后面所有
  • 如果指定两个参数,则第2个参数值表示要别删除元素的个数。
  • 如果指定三个或多个参数,则第3个以及后面所有参数都被视为插入的元素
  • 如果不执行删除操作,第2个参数值应该设置为0,但是不能够空缺,否则该方法无效
  • splice方法执行的返回值是被别除的子数组
  • 当第1个参数值大于length属性值时,被视为在数组尾部执行操作,因此删除无效,但是可以在尾部插入多个指定元素
  • 参数取负值问题。如果第1个参数为负值,则按绝对值从数组右侧开始向左侧定位。如果第2个参数为负值,则被视为0。
var arr1 = [1,2,3,4,5,6,7];
console.log(arr1.splice(4));//[5,6,7]
console.log(arr1);//[1,2,3,4]

var arr2 = [1,2,3,4,5,6,7];
console.log(arr2.splice(1,3));//[2,3,4]
console.log(arr1);//[1,5,6,7]

var arr3 = [1,2,3,4,5,6,7];
console.log(arr3.splice(2,1,"a","b","c"));//[3]
console.log(arr3);//[1,2,a,b,c,4,5,6,7]

var arr4 = [1,2,3,4,5,6,7];
console.log(arr4.splice(10,2,"a","b","c"));//[]
console.log(arr4);//[1,2,3,4,5,6,7,a,b,c]

var arr5 = [1,2,3,4,5,6,7];
console.log(arr5.splice(-5,2,"a","b","c"));//[3,4]
console.log(arr5);//[1,2,a,b,c,5,6,7]

var arr5 = [1,2,3,4,5,6,7];
console.log(arr5.splice(-5,-2,"a","b","c"));//[]
console.log(arr5);//[1,2,a,b,c,3,4,5,6,7]

使用slice()方法

  • slice方法与splice方法功能相近,但是它仅能够截取数组中指定区段的元素,并返回这个子数组
  • 该方法包含两个参数,分别指定截取子数组的起始和结束位置的下标
  • 第1个参数指定起始下标位置,包括该值指定的元素;第2个参数指定结束位置,不包括指定的元素。
  • 该方法的参数可以自由设置。如果不传递参数,则不会执行任何操作:如果仅指定一个参数,则表示从该参数值指定的下标位置开始,截取到数组的尾部所有元素
  • 当参数为负值时,表示按从右到左的顺序进行定位,即倒数定位法,而不再按正数顺序定位(从左到右),但取值顺序依然是从左到右,如果起始下标值大于或等于结束下标值,将不执行任何操作
  • sliced方法将返回数组的一部分(子数组),但不会修改原数组
var arr1 = [1,2,3,4,5,6,7,8];
console.log(arr1.slice(3));//[4,5,6,7,8]
console.log(arr1);

var arr1 = [1,2,3,4,5,6,7,8];
console.log(arr1.slice(3,6));//[4,5,6]
console.log(arr1);

var arr1 = [1,2,3,4,5,6,7,8];
console.log(arr1.slice(-3,6));//[6]
console.log(arr1.slice(-6,5));//[3,4,5]

数组排序-reverse方法

  • reverse方法能够颠倒数组元素的排列顺序,该方法不需要参数
  • 改变原来数组,不创建新数组
var arr = [1,2,"a","2","b",1,[1]];
console.log(arr.reverse());
console.log(arr);


// 手写reverse反转
function reverse(arr) {
    var newArr = [];
    for(var i in arr){
        newArr.unshift(arr[i]);
    }
    return newArr;
}
console.log(reverse([1, 4, 3, 2, 5, 1]));

数组排序 - sort方法

  • sort方法能够根据一定条件对数组元素进行排序。如果调用sort方法时没有传递参数,则按字母顺序对数组中的元素进行排序。
  • sort方法可以根据其他顺序执行操作。这时就必须为方法提供一个函数参数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字
var arr = [1,2,3,4,22,34,32,34,100];
console.log(arr.sort());
console.log(arr);

var arr = [1,2,4,3,22,34,32,34,100];
arr.sort(function (a,b) {
    console.log(a,b)
    //sort每次传入两个值,第一个参数a是后边的值,第二个参数b是前边的值
    //如果返回的正数,则两个值不交换位置,如果返回的是负数,则两个值交换位置
    if (a > b){
        return 1;
    }else if(a < b){
        return -1;
    }else{
        return 0;
    }
});
arr.sort(function (a,b) {
    return a - b;//升序排列
})
console.log(arr);

var arr = [1,2,4,3,22,34,32,34,100];
arr.sort(function (a,b) {
    if (a > b){
        return -1;
    }else if(a < b){
        return 1;
    }else{
        return 0;
    }
});

arr.sort(function(a,b){
    // return -(a - b);//降序
    return b - a;
})
console.log(arr);
  • 对对象进行排序
    // 对对象进行排序:
    var persons = [
        {name:"lily1",sex:"女",age:18},
        {name:"lily2",sex:"女",age:19},
        {name:"lily3",sex:"女",age:11},
        {name:"lily4",sex:"女",age:28},
        {name:"lily5",sex:"女",age:22},
        ]
    
    //请对数组persons进行按照年龄升序排序
    persons.sort(function (a,b) {
        if (a.age>b.age) {
            return 1;
        }else if(a.age<b.age){
            return -1;
        }else{
            return 0;
        }
    })
    console.log(persons);
  • 动态加载数据,及对象数据排序​

<!-- 动态加载数据,及对象数据排序 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="price">价格排序</button>
<button id="hot">热度排序</button>
<button id="num">销量排序</button>
<ul id="list">
    <li class="outer">
        <h2>苹果手机</h2>
        <p>销量:<span>10000</span></p>
        <p>价格:<span>11988</span></p>
        <p>热度:<span>299</span></p>
    </li>
    <li>
        <h2>苹果手机</h2>
        <p>销量:<span>10000</span></p>
        <p>价格:<span>11988</span></p>
        <p>热度:<span>299</span></p>
    </li>
    <li>
        <h2>苹果手机</h2>
        <p>销量:<span>10000</span></p>
        <p>价格:<span>11988</span></p>
        <p>热度:<span>299</span></p>
    </li>
</ul>
<script>
    var oList = document.getElementById("list");
    var oPrice = document.getElementById("price");
    var oHot = document.getElementById("hot");
    var oNum = document.getElementById("num");
    var data ={
        phone:[
            {brand:"苹果手机",num:"10000",price:"11988",hot:"199"},
            {brand:"联想手机",num:"9000",price:"1988",hot:"1199"},
            {brand:"华为手机",num:"20000",price:"12988",hot:"1299"},
            {brand:"三星手机",num:"13000",price:"7988",hot:"3199"},
            {brand:"魅族手机",num:"4000",price:"988",hot:"1099"},
            {brand:"小米手机",num:"14000",price:"19988",hot:"109"},
            {brand:"锤子手机",num:"100",price:"88",hot:"199999"},
        ],
        img:[
            "01.jpg","02.jpg","03.jpg"
        ]
    }
    var phoneDate = data.phone;
    showPhone(phoneDate);
    function showPhone(data) {
        oList.innerHTML = "";
        for(var i in data){
            var str = `
                <li>
                    <h2>${data[i].brand}</h2>
                    <p>销量:<span>${data[i].num}</span></p>
                    <p>价格:<span>${data[i].price}</span></p>
                    <p>热度:<span>${data[i].hot}</span></p>
                </li>
            `;
            oList.innerHTML += str;
        }
    }

    // 点击价格降序
    oPrice.onclick = function () {
        phoneDate.sort(function (a,b) {
            if (+a.price > +b.price){
                return -1;
            }else{
                return 1;
            }
        })
        //当数据发生变化以后,再次调用字符串拼接函数,重新向页面赋值
        showPhone(phoneDate);

    }

    // 点击热度降序
    oHot.onclick = function () {
        phoneDate.sort(function (a,b) {
            if (+a.hot > +b.hot){
                return -1;
            }else{
                return 1;
            }
        })
        //当数据发生变化以后,再次调用字符串拼接函数,重新向页面赋值
        showPhone(phoneDate);

    }

    // 点击热度降序
    oNum.onclick = function () {
        phoneDate.sort(function (a,b) {
            if (+a.num > +b.num){
                return -1;
            }else{
                return 1;
            }
        })
        //当数据发生变化以后,再次调用字符串拼接函数,重新向页面赋值
        showPhone(phoneDate);

    }

</script>
</body>
</html>

数组转换 - toString()

  • 数组中 toString把每一个元素转为字符串,然后是使用逗号连接输出显示。
  • 当数组用于字符串环境中时,JS会自动调用toString()方法把数组转为字符串。
var arr = [1,2,"a",null,{name:"lily"}];//null和undefined没有toString方法
console.log(arr.toString());//1,2,a,,[object Object]

//改写Array的toString方法
Array.prototype.toString = function () {
    alert(1);
    return "没有返回值 哈哈哈"
}
var arr = [1,2,3];
console.log(arr + "1");
// console.log(arr); 

数组转换 - toLocalString()

  • toLocalString方法和toString方法相同,主要区别在于toLocalString方法能够使用户所在地区特定的分隔符把生成的字符串连接起来。
  • 根据中国习惯,先把数字转为浮点数之后,再执行字符串
    var arr = ["1.00",222222,3,4,"a"];
    console.log(arr.toLocaleString());
    console.log(arr.toString());

数组转换 - join()

  • join方法可以把数组转换为字符串,不过它可以指定分隔符,在调用join方法时,可以传递一个参数作为分隔符来连接每个元素,如果省略参数,默认使用逗号作为分隔符,这时与toString方法转换操作效果相同。
  • 返回一个转换后的字符串,并没有改变原数组
  • 如果不想要间隔 可以输入一个空字符串
  • 如果数组的值是其他类型 在数组中 如果遇到null或者undefined 直接转换成空字符串 其他按照toString规则转换
var arr = ['1',2,'c',4,5,null];
console.log(arr.join());//返回字符串
console.log(arr);

console.log(arr.join("-"));//返回字符串
console.log(arr.join(""));//返回字符串

元素定位 - indexOf 和 lastIndexOf

  • 使用indexOf和lastIndexOf方法可以检索数组元素,返回指定元素的索引位置

  • indexof返回某个元素值在数组中的第1个匹配项的索引,如果没有找到指定的值,则返回-1.

  • 第二个参数可选,表示开始搜索的数组索引。如果省略,则从索引0处开始搜索

  • lastIndexOf返回指定的值在数组中的最后一个匹配项的索引,用法和indexOf相同

    var arr = ["xiaowang","xiaoli","xiaozhang","xiaoqian","xiaomao","xiaozhang","xiaozhang2"];
    var userStr = "xiaozhang";
    var userStr2 = "maoge";
    console.log(arr.indexOf(userStr,3));
    console.log(arr.indexOf(userStr));
    console.log(arr.indexOf(userStr,6));
    console.log(arr.indexOf(userStr2));
    

console.log(arr.lastIndexOf("xiaozhang",2));//第二个参数是限制某一个范围,返回的仍然是下标

//检测数组

//- isArray是Array类型的一个静态方法,使用它可以判断一个值是否为数组。
//- 使用 Array.isArray();
//- typeOf运算符只能显示数组的类型是Object,而 Array.isArray方法可以直接返回布尔值。在条件表达式中,使用该方法非常实用
//- 使用in运算符可以检测某个值是否存在于数组中,注意,in运算符主要用于对象,但也适用于数组


var  a = "123";
var b = [6,2,3,4];
console.log(Array.isArray(a));//false
console.log(Array.isArray(b));//true
console.log(6 in b);//false

var obj = {
    name:"xiaowang",
    sex:"nan"
}
console.log('name' in obj);

检测是否全部符合-every方法

  • every方法可以测试的所有元素是否都满足指定的测试
  • every中是函数,每一项数组的值,依次进入函数运行,函数最终要要求返回布尔值。
  • 如果有一项返回false,则停止执行,every返回false
  • 如果所有的项全部返回true 那么 整个every方法就返回true
  • every方法的回调函数拥有3个参数:1、item 数组的每一项 2、index 数组的这一项 对应的下标 3、array 原数组
  • every方法还有第二个可选的参数,可在回调函数中为其引用this关键字的对象。如果省略,则undefined是this值
// 我们得到一组年龄,如果所有的人都成年了,那么我们就开启成人网吧模式,否则 就进入儿童模式
var userAges = [18,19,19,19,22,29,50,80,45];
var isPerson = userAges.every(function (item,index,array) {
    console.log(item,index,array);
    var re = true;
    if (item < 18){
        re = false;
    }
    return re;
});
if (isPerson){
    alert("进入成人模式  坐稳了")
}else{
    alert("儿童模式")
}

// 可以简单点:
var isPerson = userAges.every(function (item,index,array) {
    return item >= 18;
});
if (isPerson){
    alert("进入成人模式  坐稳了")
}else{
    alert("儿童模式")
}


// 检测数组中的数是否都是偶数
var arr = [1,2,3,4];
var isEven = arr.every(function(item,index,arr){
    if(item % 2 == 0){
        return true;
    }else{
        return false
    }
})
if(isEven){
    console.log("偶数")
}else{
    console.log("不全是偶数")
}


//检测数组中元素的值是否都在指定的范围内,返回值用一个对象设置
var obj = {max:20,min:10};
var arr = [12,13,19];
var isScope = arr.every(function(item){
    if(typeof item !== "number"){
        return false
    }else{
        return item>=this.min && item <=this.max
    }

},obj)

检测是否存在符合 - some方法

  • 使用some方法可以确定数组的元素是否有满足指定的测试的项
  • 只要一项返回true 那么 some方法就返回true(一真即真)
  • 其余和every类似
// 我们得到一组年龄,如果所有的人都成年了,那么我们就开启成人网吧模式,否则 就进入儿童模式
var userAge = [19,18,21,23,14,45];
var isPerson = userAge.some(function (item,index,array) {
    return item < 18;
});
if (isPerson){
    alert("儿童模式")
} else{
    alert("成人模式")
}


//this指向对象的
var arr = [18,19,20,21,30,11,14,19,40];
var isTrue = arr.some(function (item,index,array) {
    // return item < 18;
    return item < this.max;
},{max:18})
console.log(isTrue);

for迭代数组

  • for和for/in语句都可以迭代数组,for语句需要配合length属性和数组的下标实现。执行效率没有for/in高。
  • for/in 也会跳过空元素
var arr = ["a","b","c",,"e"];
for (var i = 0; i < arr.length; i++) {
    console.log(arr[i]);//a b c undefined e
}

for(var j in arr){
    console.log(j,arr[j]);//a b c e
}

forEach

  • Array类型为每一个数组定义了forEach方法,可以对数组进行迭代操作
  • forEach中一个迭代函数,一个可选的this引用的对象
  • 不会对空元素调用回掉函数
  • foreach方法 用来遍历数组的每一项 forEach方法没有返回值
// 使用forEach迭代数组,计算数组元素的和

//一个家庭的年龄 过年了  每个人长1岁
//方法1 创建一个新数组
var userAages = [19,21,18,34,32,25,45];
var newUserAges = [];
userAages.forEach(function (item,index,array) {
    newUserAges[index] = item + 1;
})
console.log(newUserAges);
//方法2 直接改变原来数组
var re = userAages.forEach(function (item,index,array) {
    userAages[index] = item + 1;
})
console.log(userAages);
console.log(re);

使用keys迭代

  • keys()是Object的静态函数,专门用来遍历对象获取键名, Object.keys函数的参数是一个对象,返回一个数组,元素是该对象所有本地属性名。
  • 如果使用该函数选代数组,可以汇集数组的所有元素下标值。
  • 除了获取键名集合外,使用keys还可以间接统计对象的长度
  • Object还有一个类似的静态函数: getOwnPropertyNames,与keys用法相同,参数都是对象,返回值都是数组,数组元素都是属性名。不同点:keys仅能选代本地、可枚举的属性, getownPropertyNames可以迭代所有
var arr = ["a","b","c",,"d"];
console.log(Object.keys(arr));;

映射数组

  • 使用map方法可以对数组的每一个元素调用指定的回调函数,并返回包含结果的数组。
  • arr.map(function(){},thisArg)
  • map方法返回一个新数组,其中每一个元素均为关联的原始数组元素的回调函数返回值。
  • map不会为缺失的元素调用回调函数
//把数组中的每个元素的值平方,乘以PI的值,把返回的圆的面积作为新数组的元素值,最后返回这个新数组

// 有一个数组装着2019年所有人的年龄,2020年以后,每个人的年龄都增加1岁
var allAge = [19,15,,2,30,32,43,45];
var age2020 = allAge.map(function (item,index,arr) {
    return item+1;
})
console.log(age2020);


//回调函数可以使用系统的内置函数
var a = [25,36];
var b = a.map(Math.sqrt);
console.log(b);//[5,6]

//练习
var re = [1,2,3].map(parseInt);
console.log(re);//[1,NaN,NaN]

console.log(parseInt(1, 0));//1
console.log(parseInt(2, 1));//NaN
console.log(parseInt(3, 2));//NaN

数组过滤

  • 使用filter方法可以返回数组中满足指定条件的元素。
  • array.filter(function,thisArg)
  • 返回值是一个包含回掉函数为其返回ture的所有值的新数组。如果回调函数为array的所有元素返回false,则新数组长度为0.
  • 不会缺少的元素调用回调函数
// 筛选出所有的偶数
var arr = [1,2,3,4,5,6,6,7,8,9];
var evenArr = arr.filter(function (item,index,array) {
    return item % 2 == 0
})
console.log(evenArr);//[2,4,6,6,8]

function f(item,index,array) {
    return item % 2 == 0;
}
var evenArr = arr.filter(f);
console.log(evenArr);


// 使用filter过滤掉不符合条件的值
var obj = {max:80,min:14};
var age = [12,11,34,56,90,92,65,65,34,31];
var newAge = age.filter(function (item) {
    return item >= this.min && item <= this.max;
},obj)
console.log(newAge);

reduce

  • reduce方法可对数组的所有元素调用指定的回调函数。
  • 该回调函数的返回值为累计结果,并且此返回值在下一次调用该回调函数的时候作为参 数提供
  • reduce方法的参数函数可以是4个参数:上一次调用回调函数的返回值,如果提供了reduce的第二个参数,则第一次调用回调函数时,初始值是reduce方法的第二个参数;当前元数组元素的值;元素索引;数组对象
  • reduce方法的最终返回值是通过最后一次调用回调函数获得的累计结果。
// 用-把数组的各个值分开‘
var arr = ["abc","def","ggg","uuu"];
var newStr = arr.reduce(function (a,b,c,d) {
    return a + "-" + b;
},"oh")
console.log(newStr);//oh-abc-def-ggg-uuu
// 数组 arr = [1,2,3,4] 求数组的和
var arr = [1,2,3,4];
arr.reduce(function(pre,cur){return pre + cur}); // return 10

// 计算数组最大值
var max = arr.reduce(function (prev, cur) {
    return Math.max(prev,cur);
});

数组练习

  • 将数组的0去掉
  • 数组排序去重
  • 返回数组的最大最小值
  • 求一组数中的所有数的和和平均值
  • 求一组数中的最大值和最小值,以及所在位置
  • 翻转数组
  • 冒泡排序,从小到大
// 将数组的0去掉
var arr = [1,3,4,0,6,4,0,4,5];
var newArr = [];
arr.forEach(function (item,index,array) {
    if (item == 0){
        // 当等于0的时候,我们不是要退出循环 而是退出这个函数
        return;
    }
    newArr.push(item);
})
console.log(newArr);
// 数组排序去重
var arr = [1,3,2,2,4,2,1,6,4,2];
// 先排序 后去重

arr.sort(function (a,b) {
    return a-b;
})
console.log(arr);
var newArr = [];
for (var i = 0; i < arr.length; i++) {
    if (arr[i] == arr[i+1]){
        continue;
    }
    newArr.push(arr[i]);
}
console.log(newArr);
var arr = [1,3,56,43,78,32,67,34,0,87];
var min = 100;
var max = 0;
for (var i = 0; i < arr.length; i++) {
    if (min > arr[i]){
        min = arr[i];
    }
    if (max < arr[i]){
        max = arr[i];
    }
}
console.log(min,max)

JS字符串

基本包装类型

  • 为了方便操作简单数据类型,JavaScript还提供了三个特殊的简单类型类型:String/Number/Boolean

  • 实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据

    var s1 = "some text";
    var s2 = s1.substring(2);
    
    //以上过程分为三步
    //1.创建 String 类型的一个实例;
    //2.在实例上调用指定的方法;
    //3.销毁这个实例。
    
    var s1 = new String("some text");
    var s2 = s1.substring(2);
    s1 = null;
    
    
    // 为什么我们不能给基本类型扩展属性或方法
    var str = "1234444";
    str.name = "hello";
    // 解析如下:
    var _str = new String("1234444");
    _str.name = "hello";
    _str = null;

concat方法

  • 使用字符串的concat的方法可以把多个参数添加到指定字符串的尾部
  • 参数类型和个数没有限制,把所有参数转换为字符串,返回连接以后的字符串
  • 特定情况下,可以使用加法 和 数组的join 拼接字符串
  • 原本数组的join方法代替concat方法,速度很快。但是当代浏览器优化加号拼接。所以推荐使用加号拼接
var str = "hello";
var _str = str.concat("world","oh");
console.log(str);//hello
console.log(_str);//helloworldoh


var str2 = "hello";
var _str2 = str.concat("world",['oh','nice'],{name:"laowang"});
console.log(str2);//hello
console.log(_str2);//helloworldoh,nice[object Object]


var straLL = "0123456789abcdefghijklmnopqrstuvwxyz";
var strArr = [];
for (var i = 0; i < 20; i++) {
    var strNum = Math.floor(Math.random() * straLL.length)
    strArr.push(straLL[strNum]);
}
var finallyStr = strArr.join("");
console.log(finallyStr);

字符串查找

  • charAt(n) 返回字符串中的第n个字符,参数如果不是0 --- length-1 之间,则返回空字符串
  • indexOf和lastIndexOf:可以根据参数字符串,返回指定子字符串的下标位置。 两个方法都有两个参数,第一个参数查找的目标,第二个参数是起始位置
  • search 方法于indexOf的功能是相同的,查找指定字符串第一次出现的位置,但是只有一个参数,定义匹配模式,没有反向检索和全局模式。
  • macth方法能够找出所有匹配的子字符串,并以数组的形式返回,如果匹配模式没有指定全局,则进行1次匹配。如果没找到则返回null
// 01.charAt
var str = "hello i am fine thankyou";
console.log(str.charAt(8));//a
console.log(str.charAt(0));//a
console.log(str.charAt(str.length));//空字符串

//02.indexOf()和lastIndexOf()
var str = "hello i am fine thankyou";
console.log(str.indexOf("a"));//8
console.log(str.lastIndexOf("a"));//18
console.log(str.indexOf("a",9));//18
console.log(str.lastIndexOf("a",5));//-1  倒着查  5是下标

// 03.search();
var str = "110p1101h110";
var index1 = str.search(/[a-zA-Z]/g);
console.log(index1);

// 04.match()
var str = "110p1101h110";
var index2 = str.match(/[a-zA-Z]/g);
console.log(index2);//["p","h"]

字符串截取

  • substr方法:substr能够根据指定长度来截取子字符串。包含两个参数,一个是起始下标,一个是截取的长度。省略第二个参数则代表末尾(ECMA不再推荐该方法)
  • slice和substring方法都是根据指定的起止下标来截取字符串的,包含两个参数,一个是开始下标,一个是结束下标(不包含),第二个参数省略则代表末尾
  • 起始和结束位置相比较的大小无法确定的时候可以使用substring。
  • 如果为负值,slice能把负值当作从右向左。而substring视其无效
var str = "today is a fine day";
var _str = str.substr(4,5);
console.log(str);//today is a fine day
console.log(_str);//y is
var _str = str.substr(4);
console.log(_str);//y is a fine day


var str2 = "today is a fine day";
var _str2 = str2.substring(4,8);
console.log(str2);//today is a fine day
console.log(_str2);//y is
var _str2 = str2.substring(4);
console.log(_str2);//y is a fine day

var str3 = "today is a fine day";
var _str3 = str3.slice(4,8);
console.log(str3);//today is a fine day
console.log(_str3);//y is
var _str3 = str3.slice(4);
console.log(_str3);//y is a fine day


var str3 = "today is a fine day";
var _str3 = str3.slice(4,-11);
console.log(str3);//today is a fine day
console.log(_str3);//y is a

var str2 = "today is a fine day";
var _str2 = str2.substring(4,-1);
console.log(str2);//today is a fine day
console.log(_str2);//toda

字符串大小转换

  • toLowerCase():将字符串转换成小写
  • toUpperCase():将字符串转换成大写
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>字符串的大小写转换</title>
    <style>
        #ipt{
            width: 200px;
            height: 50px;
            border: 1px solid red;
        }
    </style>
</head>
<body>
<div id="outer">
    请输入验证码:
    <input type="text" id="ipt">
    <button id="con">hj76tr</button>
</div>
<script>
    /*
    *   toLowerCase() 转成小写字母
    *   toUpperCase() 转成大写字母
    * */
    var oCon = document.getElementById("con");
    var oIpt = document.getElementById("ipt");

    oIpt.onchange = function () {
        if (oIpt.value.toLowerCase() === oCon.innerHTML) {
            oIpt.style.borderColor = "green";
        }
    }
</script>
</body>
</html>

字符串与数组转换

  • 使用字符串的split方法可以根据指定的分割符把字符串切分成数组
  • 如果参数是空字符串,则按照单个字符切分,返回和字符串等长的数组
  • 如果没有参数,则把字符串当作返回数组的一个值
  • 支持第二个参数,代表数组的最大长度
    var str = "哎,呦,不,错,哦";
    var arr = str.split();
    var arr2 = str.split('');
    var arr3 = str.split(',');
    var arr4 = str.split(',',3);
    console.log(str);
    console.log(arr);//["哎,呦,不,错,哦"]
    console.log(arr2);// ["哎", ",", "呦", ",", "不", ",", "错", ",", "哦"]
    console.log(arr3);//["哎", "呦", "不", "错", "哦"]
    console.log(arr4);// ["哎", "呦", "不"]

清除两侧空字符串

  • ES5新增了trim方法,用以从字符串中移除前导空字符和尾部空字符和行行终止符
  • 常用在表单
var str1 = "   kajsbd  asknj df asf adf  \n";
console.log(str1.trim());

将字符串转换成对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>将字符串转换成对象</title>
</head>
<body>
<script>
    // http://www.baidu.com?username=xiaowang&password=12345&age=18&sex=女&score=90
    // {username:"xiaowang",password:12345,age:18,sex:"女",score:90}

    var url = "http://www.baidu.com?username=xiaowang&password=12345&age=18&sex=女&score=90";
    var obj = {};
    var strArr = url.split("?");
    // console.log(strArr[1]);//username=xiaowang&password=12345&age=18&sex=女&score=90
    var strArr2 = strArr[1].split("&");
    // console.log(strArr2);//["username=xiaowang", "password=12345", "age=18", "sex=女", "score=90"]
    for (var i = 0; i < strArr2.length; i++) {
        // strArr2[i].split("=")//["username","xiaowang"]
        obj[strArr2[i].split("=")[0]] = strArr2[i].split("=")[1];
    }
    console.log(obj);//{username: "xiaowang", password: "12345", age: "18", sex: "女", score: "90"}



    // var str = 'username=xiaowang&password=12345&age=18&sex=女&score=90'
    function queryString(str) {
        var obj = {};
        var strArr2 = str.split("&");
        for (var i = 0; i < strArr2.length; i++) {
            obj [ strArr2[i].split("=")[0] ] = strArr2[i].split("=")[1];
        }
        return obj;
    }
</script>
</body>
</html>

对象转字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>将对象转换成字符串</title>
</head>
<body>
<script>
    // 已知我们得到的对象是:{username: "xiaowang", password: "12345", age: "18", sex: "女", score: "90"}
    // 已知被拼接的url地址是:http://www.baidu.com
    // 最终得到:http://www.baidu.com?username=xiaowang&password=12345&age=18&sex=女&score=90

    var obj = {username: "xiaowang", password: "12345", age: "18", sex: "女", score: "90"};
    var url = "http://www.baidu.com";

    // 第一个目标:["username=xiaowang", "password=12345", "age=18", "sex=女", "score=90"]
    var arr = [];
    for(var i in obj){
        var str = i + "=" + obj[i];
        // console.log(str);//username=xiaowang
        arr.push(str);
    }
    // console.log(arr); //["username=xiaowang", "password=12345", "age=18", "sex=女", "score=90"]

    // 第二个目标:username=xiaowang&password=12345&age=18&sex=女&score=90

    var newStr = arr.join("&");
    console.log(newStr);

    var newUrl = url + "?" + newStr;
    console.log(newUrl);
</script>
</body>
</html>

Math对象

Math对象是JavaScript的内置对象,提供一系列的数学常数和数学方法。Math对象只提供了静态的属性和方法,所以使用时,不需要实例化。

JS对象的分类:

内部对象:

  • js中的内部对象包括Array、Boolean、Date、Function、Global、Math、Number、Object、RegExp、String以及各种错误类对象,包括Error、EvalError、RangeError、ReferenceError、SyntaxError和TypeError。
  • 其中Global和Math这两个对象又被称为“内置对象”,这两个对象在脚本程序初始化时被创建,不必实例化这两个对象。

宿主对象

  • 宿主对象就是执行JS脚本的环境提供的对象。对于嵌入到网页中的JS来说,其宿主对象就是浏览器提供的对象,所以又称为浏览器对象,如IE、Firefox等浏览器提供的对象。不同的浏览器提供的宿主对象可能不同,即使提供的对象相同,其实现方式也大相径庭!这会带来浏览器兼容问题,增加开发难度。 浏览器对象有很多,如Window和Document,Element,form,image,等等。

自定义对象

  • 顾名思义,就是开发人员自己定义的对象。JS允许使用自定义对象,使JS应用及功能得到扩充

Math对象的属性

Math对象提供以下一些只读的数学常数。

  • Math.E // 2.718281828459045(自然底数 无限不循环小数)lim(1+1/n)^n=e ,n→+∞
  • Math.LN2 // 0.6931471805599453(2的自然对数)
  • Math.LN10 // 2.302585092994046(10的自然对数)
  • Math.LOG2E // 1.4426950408889634(2为底 e的对数)
  • Math.LOG10E // 0.4342944819032518
  • Math.PI // 3.141592653589793
  • Math.SQRT1_2 // 0.7071067811865476
  • Math.SQRT2 // 1.4142135623730951

练习:​​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Math对象的属性</title>
</head>
<body>
<h2>请输入你家花园的半径 单位是m</h2>
<input type="text" id="ipt">
<h2>你家花园的面积是 <span id="area"></span>,  你家花园的周长是 <span id="length"></span></h2>
<script>
    // 获取π
    console.log(Math.PI);
    //获取自然底数
    console.log(Math.E);
    //获取2的和1/2平方根
    console.log(Math.SQRT2);
    console.log(Math.SQRT1_2);

    // 练习
    var oIpt = document.getElementById("ipt");
    var oArea = document.getElementById("area");
    var oLength = document.getElementById("length");
    oIpt.onchange = function () {
        oArea.innerHTML = Math.PI * oIpt.value * oIpt.value;
        oLength.innerHTML = 2 * Math.PI * oIpt.value;
    }
</script>
</body>
</html>

Math对象的方法

  • Math.abs():求绝对值 对一个数字进行绝对值操作,并且也能对纯数字的字符串操作
  • Math.ceil():向上取整 向大的数值取整
  • Math.floor():向下取整 向小的数值取整
  • Math.round():四舍五入,四舍五入是对小数点的后一位进行判断
    • 正数的时候,当小数点后一位大于等于5的时候 整数部分加1 小于5的时候 整数部分不变
    • 负数的时候 当小数点后一位 大于5的时候 整数减1 小于5的时候 整数不变
    • 负数的时候 当小数点后一位是5 后边没有第二个小数位了 整数不变;如果5后还有小数位 那么 整数减1
  • Math.min()和Math.max() 计算一组数值中的最大值和最小值
  • pow:返回以第一个参数为底数,第二个参数为幂的指数值
  • sqrt: 返回参数的平方根,如果参数是负数,返回NaN
  • sin cos tan
  • Math.random():求随机数 生成 [0,1) (大于等于0 小于1) 小数随机数
console.log(Math.abs(4));;//4
console.log(Math.abs(-4));;//4
console.log(Math.abs(-Infinity));;//Infinity
console.log(Math.abs("-3px"));;//NaN
console.log(Math.abs("-3"));;//3

console.log(Math.ceil(3.1));//4
console.log(Math.ceil(3.5));//4
console.log(Math.ceil(-3.1));//-3
console.log(Math.ceil(-3.5));//-3

console.log(Math.floor(3.1));//3
console.log(Math.floor(3.5));//3
console.log(Math.floor(-3.1));//-4
console.log(Math.floor(-3.5));//-4

console.log(Math.round(3.1));;//3
console.log(Math.round(3.5));;//4
console.log(Math.round(3.55));;//4
console.log(Math.round(3.6));;//4
console.log(Math.round(-3.1));;//-3
console.log(Math.round(-3.5));;//-3
console.log(Math.round(-3.51));;//-4
console.log(Math.round(-3.6));;//-4

console.log(Math.max(45, 32, 45, 65, 12, 3, 8, 45, 33));
console.log(Math.min(45, 32, 45, 65, 12, 3, 8, 45, 33));

console.log(Math.pow(2, 200));
console.log(Math.pow(2.1, 200));

// 1度的弧度 :2 * Math.PI / 360
console.log(Math.sin(30 * 2 * Math.PI / 360));

console.log(Math.sqrt(4));

setInterval(function () {
    console.log(Math.random());
},300)

随机数练习

  • 得到[a,b)的随机数
  • 得到一个随机字符串
  • 如何得到一个随机的IP地址:0.0.0.0~255.255.255.255
  • 有一半的几率在做什么事情,另一半的几率在做另一件事
//得到[a,b)的随机数
function random(a,b) {
 return a + Math.floor(Math.random()*(b-a));
}

// 得到一个随机字符串
function randomStr(len){
    var dict = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    var str = '';
    for(var i = 0; i < len; i ++){
        str += dict[random(0,62)]//获取数组的下表所对应的元素
    }
    return str;
}

//如何得到一个随机的IP地址:0.0.0.0~255.255.255.255
function randomIP(len){
    var arr = []
    for(var i = 0; i < 4; i++){
        arr.push(random(0,256));
    }
    return arr.join('.');
}

//有一半的几率在做什么事情,另一半的几率在做另一件事
if(Math.random() > 0.5){
    console.log('haha');
}else{
    console.log('ninini');
}

小球曲线运动

​​

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>小球曲线运动</title>
    <style>
        #box{
            width: 20px;
            height: 20px;
            background-color: red;
            border-radius: 50%;

            position: fixed;
            left: 20px;
            top: 300px;
        }
    </style>
</head>
<body>
    <div id="box">

    </div>
    <script>
        var oBox = document.getElementById("box");
        var timer = null;
        var degStart = 0;
        timer = setInterval(function () {
            degStart ++;
            oBox.style.left = 20 + degStart + "px";
            oBox.style.top = 300 + Math.sin(degStart * 2 * Math.PI / 360)*100 + "px";
        },10)
    </script>
</body>
</html>

小球曲线运动路径版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>小球曲线运动</title>
    <style>
        #box{
            width: 20px;
            height: 20px;
            background-color: red;
            border-radius: 50%;

            position: fixed;
            left: 20px;
            top: 300px;
        }
        .show{
            width: 20px;
            height: 20px;
            background-color: red;
            border-radius: 50%;
            position: fixed;
        }
    </style>
</head>
<body>
    <div id="box">

    </div>
    <script>
        var oBox = document.getElementById("box");
        var timer = null;
        var degStart = 0;
        timer = setInterval(function () {
            var newBox = document.createElement("div");
            newBox.className = "show";
            document.body.appendChild(newBox);
            degStart ++;
            newBox.style.left = 20 + degStart + "px";
            // newBox.style.top = 300 + Math.sin(degStart * 2 * Math.PI / 360)*100 + "px";
            // newBox.style.top = 300 + -Math.abs(Math.sin(degStart * 2 * Math.PI / 360)*100) + "px";
            newBox.style.top = 300 + -(Math.sin(degStart * 2 * Math.PI / 360)*100) + "px";
        },10)
    </script>
</body>
</html>

点名器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>点名器</title>
    <style>
        body{
            background-color: #0D1635;
        }
        h2{
            font-size: 70px;
            color: yellow;
            text-align: center;
        }
        p{
            font-size: 60px;
            color: #fff;
            text-align: center;
        }
    </style>
</head>
<body>
    <h2 id="btn">start</h2>
    <p id="con">开始点名</p>
    <script>
        var oBtn = document.getElementById("btn");
        var oCon = document.getElementById("con");
        var names = ["陈纪法","张三","李四","胡宝林","柏洪洋"];
        var flag = true;//保存当前停止状态
        var timer = null;
        oBtn.onclick = function () {
            if(flag){
                oBtn.innerHTML = "stop";
                timer = setInterval(function () {
                    // names中的值应该是[0,4]  [0,5)
                    oCon.innerHTML = names[Math.floor(Math.random()*names.length)];
                },30)
            }else{
                oBtn.innerHTML = "start";
                clearInterval(timer);
            }

            flag = !flag;
        }
    </script>
</body>
</html>

Date对象

获取时间

Date用来处理日期和时间的

// 获取当前的时间
var now = new Date();
console.log(now)//Fri Aug 02 2019 14:20:09 GMT+0800 (伊尔库茨克标准时间)
console.log(typeof now)//对象

创建一个时间 new Date(指定时间)

  • 传入一个毫秒数(会把毫秒数转换成一个时间c 然后加上1970年1月1日 8:00 的时间)
  • 传入一个字符串格式的时间 2028-10-2 1029
  • 传入多个参数 分别代表年 月 日 时 分 秒 毫秒
var date1 = new Date(1000 * 60 * 60);
var date1 = new Date(1546354578234);
console.log(date1);

var date2 = new Date("2019-10-01 8:0:0");
var date2 = new Date("2019-10-1");//如果不写时间 只写年月日 那么时间按照00点来计算
var date2 = new Date("8:0:0");//Invalid Date 如果不写年月日 那么时间是错误的
console.log(date2);

var date3 = new Date(2019,9,1,8,10,20,300);//当数字形式传递或者获取月份的时候,月份是从0开始算  0 代表 1月
var date3 = new Date(2019,16,1,8,10,20,300);//时间超出分为 会自动向前进一位  但是不建议这么写
console.log(date3);

获取时间

时间对象 无论是当前的时间还是我们设置的时间 都拥有获取精确年月日等等单独时间的方法。

  • 获取年份 getFullYear
  • 获取月份 getMonth
  • 获取日 getDate
  • 获取小时 getHours
  • 获取分钟 getMinutes
  • 获取秒 getSeconds
  • 获取毫秒 getMilliseconds
  • 获取星期 getDay
  • 获取当前距离1970年1月1日800 的毫秒数 getTime
var now = new Date();

//获取年份
var nowYear = now.getFullYear();
// var nowYear = now.getYear();//只获取后两位  2000年之前的方法。我们不推荐使用
console.log(nowYear);//2019
console.log(typeof nowYear);//number

//获取月份
var nowMonth = now.getMonth();
console.log(nowMonth);//7  代表8月  月份从0开始计算

//获取日
var nowDate = now.getDate();
console.log(nowDate);//2

// 获取小时
var nowHours = now.getHours();
console.log(nowHours);//24小时时间

//获取分钟
var nowMinutes = now.getMinutes();
console.log(nowMinutes);//42

// 获取秒
var nowseconds = now.getSeconds();
console.log(nowseconds)//11

//获取毫秒
var nowMilliseconds = now.getMilliseconds();
console.log(nowMilliseconds);

// 获取星期
var nowDay = now.getDay();
console.log(nowDay);//5

//获取当前距离1970年1月1日8:00:00 的毫秒数
var nowTime = now.getTime();
console.log(nowTime);

北京时间练习

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>北京时间练习</title>
</head>
<body>
<div id="time">
<!--    当前时间是 2019 年 08 月 02 日 14 时 46 分 40秒 018毫秒-->

</div>
<script>
    var oTime = document.getElementById("time");
    //给 月 日 时 分 秒 添加0的函数
    function addZero(n) {
        return n>10 ? n : "0"+n;
    }
    function getNowTime() {
        var now = new Date();

        var y = now.getFullYear();

        var m = now.getMonth() + 1;
        m = addZero(m);

        var d = now.getDate();
        d = addZero(d);

        var h = now.getHours();
        h = addZero(h);

        var min = now.getMinutes();
        min = addZero(min);

        var s = now.getSeconds();
        s = addZero(s);

        var mili = now.getMilliseconds();
        if (mili < 10){
            mili = "00" + mili;
        } else if (mili<100){
            mili = "0" + mili;
        }

        var nowTime = y + "年" + m + "月" + d + "日" + h + "时" + min + "分" + s + "秒" + mili + "毫秒"
        oTime.innerHTML = nowTime;
    }

    var timer = setInterval(getNowTime,1);
</script>
</body>
</html>

设置单独的时间

  • 设置年份 setFullYear
  • 设置月份 setMonth
  • 设置日 setDate
  • 设置小时 setHours
  • 设置分钟 setMinutes
  • 设置秒 setSeconds
  • 设置毫秒 setMilliseconds
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设置时间</title>
</head>
<body>
<script>
    var now = new Date();

    //设置年份
    now.setFullYear(2023);
    console.log(now);

    //设置月份
    now.setMonth(0);
    console.log(now);

    //设置日期
    now.setDate(4);
    console.log(now);

    //设置 小时
    now.setHours(19);
    console.log(now);

    //设置分钟
    now.setMinutes(40);
    console.log(now);

    //设置秒
    now.setSeconds(10);
    console.log(now);

    //设置毫秒
    now.setMilliseconds(100);
    console.log(now)

    //设置星期  不允许 没有这个方法
    now.setDay();
    console.log(now);
</script>
</body>
</html>

设置时间练习

设置一个3天后的当前时间

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设置时间练习</title>
</head>
<body>
<script>
    //设置一个3天后的当前时间
   /* //获取当前时间
    var now = new Date();
    //获取当前时间的日期
    var nowDate = now.getDate();
    //让当前的日期 加上3
    newDate = nowDate+3;

    // 获取一个当前的时间,然后把当前时间的日期 设置为newDate
    // console.log(newDate)
    var newTime = new Date();
    newTime = newTime.setDate(newDate);
    console.log(newTime);*/


    var threeDatLast = new Date();
    threeDatLast.setDate(new Date().getDate()+3);
    console.log(threeDatLast.toLocaleString());
</script>
</body>
</html>

获取时间戳

时间戳:1970年1月1日 8点距离现在的毫秒数

// 获取时间戳
//方法1:  通过getTime()
var now = new Date();
var time = now.getTime();
console.log(time);

// 方法二 将时间对象转换成数字   Number  一元运算
var now = new Date();
var time = Numb er(now);
console.log(time);

var time = + now;
console.log(time);

var time = parseInt(now);//parseInt不能转
console.log(time);

//方法三  valueOf 返回当前对象的最原始的值
var time = now.valueOf();
console.log(time);

//方法四 Date对象 不用实例化  直接拥有一个now方法  返回当前的毫秒数
var time = Date.now();
console.log(time);

计算代码运行时间1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算代码运行时间</title>
</head>
<body>
<script>
    var start = new Date();

    var num = 0;
    for (var i = 0; i < 100000000; i++) {
        num += i;
    }

    var end = new Date();

    // 两个时间对象相减 可以直接得到毫秒数
    console.log(end - start);
</script>
</body>
</html>

计算代码运行时间2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算代码运行时间</title>
</head>
<body>
<script>
    console.time("name");
    var num = 0; 
    for (var i = 0; i < 100000000; i++) {
        num += i;
    }
    console.timeEnd("name");

    /* console.time() 中需要传递一个参数 代表当前计算时间的代号
     console.timeEnd() 中需要传递一个参数 查找相同参数的 time   然后计算出来之间的差值时间  并打印*/
</script>
</body>
</html>

倒计时效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>倒计时</title>
</head>
<body>
<div id="con">

</div>
<script>

    function fn1() {
        //获取距离国庆节还有多少时间
        var guoqing = new Date(2019,9,1,8,0,0);//国庆节时间
        var now = new Date();//当前时间
        var reduceTime = guoqing - now;//距离国庆假还有多少毫秒数
        // console.log(reduceTime)h;

        // 获取天
        var date = parseInt(reduceTime / (1000 * 60 * 60 * 24));
        date < 10 ? date = "0" + date : date;

        // 获取小时
        var _date = reduceTime % (1000 * 60 * 60 * 24);
        var hours =parseInt( _date / (1000 * 60 * 60 ));
        hours < 10 ? hours = "0" + hours : hours;


        // 获取分钟
        var _hours = _date % (1000 * 60 * 60);
        var minu =parseInt( _hours / (1000 * 60 ));
        minu < 10 ? minu = "0" + minu : minu;

        // 获取秒
        var _minu = _hours % (1000 * 60);
        var sec =parseInt( _minu / (1000 ));
        sec < 10 ? sec = "0" + sec : sec;

        var str = "距离国庆假还有" + date + "天" + hours + "小时" + minu + "分" + sec + "秒";
        document.getElementById("con").innerHTML = str;
    }
    setInterval(fn1,1000);
</script>
</body>
</html>

正则表达式

一 正则表达式概述

1.1 什么是正则表达式

  • 正则表达式本身就是一种语言,这在其他语言中是通用的
  • 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符串、及这些特定字符串的组合,组成的一个“规则字符串”,这个规则字符串用来表达对字符串的一种过滤逻辑。
  • 正则就是规则,让计算机读懂我们的规则

1.2 正则表达式能做什么

  • 假设用户需要在 HTML 表单中填写姓名、地址、出生日期等
  • 那么在将表单提交到服务器进一步处理前, JavaScript 程序会检查表单以确认用户确实输入了信息并且这些信息是符合要求的

1.3 JS中的正则表达式

  • 正则表达式 (regular expression)是一个描述字符模式的对象。
  • ECMAScript 的 RegExp 类表示正则表达式
  • 正则表达式主要用来验证客户端的输入数据
  • 用户填写完表单单击按钮之后 ,表单就会被发送到服务器,在服务器端通常会用 PHP、ASP.NET等服务器脚本对其进行进一步处理
  • 因为客户端验证,可以节约大量的服务器端的系统资源,并且提供更好的用户体验

二、正则表达式的使用

2.1 创建正则表达式

  • 创建正则表达式和创建字符串类似 ,创建正则表达式提供了两种方法

    • 一种是采用 new 运算符,

      var reg = new RegExp('box', 'ig'); //第二个参数可选模式修饰符

    • 另一个是采用字面量方式

      var reg = /box/ig; //在第二个斜杠后面加上模式修饰符 g:代表可以进行全局匹配。正则默认匹配到就不匹配了 i:代表不区分大小写匹配。

// 正则表达式定义方法1
var reg1 = new RegExp("abc");
console.log(reg1);// /abc/
console.log(typeof reg1);//object
console.log(typeof new Date());//object
console.log(typeof new String("12345"));//object

var str = "12beadbabcre";
console.log(str.search(reg1));//7

// 正则定义方法2
//  \d 匹配所有的数字
var reg2 = /\d/;
var str2 = "ab3er4b6b4";
console.log(str2.search(reg2));

2.2 正则表达式的测试方法

2.2.1 正则对象方法

RegExp 对象包含两个方法:test()和 exec(),功能基本相似,用于测试字符串匹配。

  • test() 方法在字符串中查找是否存在指定的正则表达式并返回布尔值 ,如果存在则返回 true,不存在则返回 false。

    //判断一个字符串是不是全数字
    //判断一个字符串有没有数字
    
    // \d代表匹配数字  \D
    var oIpt = document.getElementById("ipt");
    var reg1 = /\d/;
    var reg2 = /\D/;
    oIpt.onchange = function () {
        var userCon = this.value;
        // 如果输入的含有数字则弹出 包含数字
        /*if (reg1.test(userCon)){
                alert("包含数字")
            } else {
                alert("没有数字")
            }*/
    
        // 判断是不是都是数字  \D一个都匹配不到,则全部是数字
        if (reg2.test(userCon)){
            alert("不全是数字");
        } else{
            alert("全是数字")
        }
    }
  • exec()方法也用于在字符串中查找指定正则表达式,如果 exec()方法执行成功,则返回包含该查找字符串的相关信息数组。如果执行失败,则返回 null

    var reg = /\d/g;//g是全局匹配
    var str = "aabb223";
    /*exec方法  对字符串进行查找,每次调用只会查找一次,查找到1个以后,便停止查找
        然后把小标移到查找元素的后一位。
        返回查找到信息的相关数组
    */
    console.log(reg.exec(str));
    console.log(reg.exec(str));
    console.log(reg.exec(str));
    console.log(reg.exec(str));
    
    // 需求:当表单失去焦点以后,把用户输入的信息过滤,把所有的数字那出来组成一个新的数字字符串、
    var oText = document.getElementById("text");
    oText.onchange = function () {
        var reg1 = /\d/g;
        var userCon = oText.value;
        var newStr = '';
        /*while(reg.exec(userCon)){
                newStr += reg.exec(userCon)[0];
            }*/
        do{
            var a = reg1.exec(userCon);
            if (a){
                newStr += a[0]
            } else{
                break;
            }
        }while(a);
        console.log(newStr);
    }
2.2.2 字符串对象方法
  • search(pattern)返回字符串中 pattern 开始(第一次出现)位置

    var reg = /g/g;
    var str = "bjkgdlahxjqgasj;lj112!@#$#%#!#";
    console.log(str.search(reg));
    
    // search和indexOf  都是返回匹配元素的出现的位置
    //     1、search方法可以传递正则  indexOf只能传递要匹配的字符串
    //     2、indexOf是更为底层的方法,如果说执行相同的查找,indexOf的效率更高,如果不使用正则匹配 ,那么建议使用indexOf
  • match(pattern)返回 pattern 中的子串或 null

    var reg = /\d/g;
    // var reg = /zzz/g;
    var str = "3h45h3gg4gf3";
    // match 返回匹配项目的集合(正则开启全局)  是一个数组
    // 如果匹配不到则返回null
    console.log(str.match(reg));
  • replace(pattern, replacement)用 replacement 替换 pattern

    /*var str = "  wad sdf e3 3 d s";
      var newStr = str.replace(" ","");//匹配到所有的空格 然后用空字符串替换
      console.log(str);
      console.log(newStr);
    */
    
    var str = "  wad sdf e3 3 d s";
    // \s代表空格
    var reg = /\s/g;
    var newStr = str.replace(reg,"");//匹配到所有的空格 然后用空字符串替换
    console.log(str);
    console.log(newStr);
    
    //和谐敏感词
    var oText = document.getElementById("text");
    oText.onchange = function () {
        var reg = /滚蛋|傻逼|傻屌一个|狗日的/g;
        var userCon = oText.value;
        // var newStr = userCon.replace(reg,"**");
    
        var newStr = userCon.replace(reg,function (i) {
            var len = i.length;
            var str = "";
            for (var j = 0; j < len; j++) {
                str += "*";
            }
            return str;
        });
        oText.value = newStr;
    }
  • split(pattern)返回字符串按指定 pattern 拆分的数组

    var  str = "asd3fgh4jkl5hg6hu7a";
    // var str = "1qazxcvbnm,./']="
    
    var arr = str.split("g");
    console.log(arr);
    
    var reg = /\d/g;
    var arr = str.split(reg);
    console.log(arr);

三 正则表达式的使用

3.1 中括号

中括号代表一个整体,并且括号中的字符属于或者关系,只挑选一个出来匹配

  • [abc] : 查找方括号之间的任何字符。
  • [ ^ abc] : 查找任何不在方括号之间的字符。
  • [0-9] : 查找任何从 0 至 9 的数字。
  • [a-z] : 查找任何从小写 a 到小写 z 的字符。
  • [A-Z] : 查找任何从大写 A 到大写 Z的字符。
var reg = /[abc]/g;
var str = "ancbgfbdc";
console.log(str.match(reg));

var reg2 = /[狗猫羊]/g;
// var reg2 = /狗|猫|羊/g
var str = "我家买了两个狗供我上学,然后我家猫不开心,和羊在一起了";
console.log(str.match(reg2));

var reg3 = /[0-9]+/g;
// var reg3 = /\d/g;
var str = "ajsbf382012bnmqsd192039e";
console.log(str.match(reg3));

var reg4 = /[0-9狗猫羊a-z]+/g;
var str4 = "89猫毛狗毛haha";
console.log(str4.match(reg4));

var reg5 = /[a-zA-Z0-9_\\]{10,15}/;
var str = "lph1\\256_789";
console.log(reg5.test(str));

var reg6 = /[^A-Z]+/g;
var str = "HHGG";
console.log(reg6.test(str));//false

3.2 转义字符

  • 正则表达式元字符是包含特殊含义的字符
  • . : 匹配任意一个字符,除了换行符( \n )
  • \b : 匹配一个单词边界,不匹配任何字符,只是匹配一个位置(一边数数字字母下划线,另一边必须是开头位置结束位置以及非数字字母下划线)
  • \d : 匹配任意一个数字,0~9 中的任意一个
  • \D : 匹配任意一个非数字
  • \s : 匹配任意一个空白字符
  • \S:匹配非空格
  • \w : 匹配任意一个字符( 字母、 数字、下划线 )
  • \W:匹配非字母、 数字、下划线 )[^a-zA-Z0-9_]
  • \n : 查找换行符
// 将类名box1替换成box4
var arr = ["box1","box1 box2","box1 box3","box1box2"]
// var arr = ["box4","box4 box2","box4 box3","box1box2"]

var newArr = arr.map(function (item,index) {
    // \b单词边界
    var reg1 = /\bbox1\b/g;
    return item.replace(reg1,"box4");
})
console.log(newArr);

3.3 获取控制 量词

  • n+ : 匹配任何包含至少一个 n 的字符串。
  • n? : 匹配任何包含零个或一个 n 的字符串。
  • n* : 匹配任何包含零个或多个 n 的字符串。
  • n{X} : 匹配包含 X 个 n 的序列的字符串。
  • n{X,Y} : 匹配包含 X 到Y 个 n 的序列的字符串。(y可以不写)
  • ^n : 匹配任何开头为 n 的字符串。
  • n$ : 匹配任何结尾为 n 的字符串。
  • | : 匹配左边或者右边
  • \ : 转义符
  • /[\u4E00-\u9FA5]/包含中文字符正则

四 案例

  • 通用的邮箱标准: 长度不限,可以使用英文(包括大小写)、数字、点号、下划线、减号,首字母必须是字母或数字;

    "^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-z]{2,}$“

  • 手机号验证

    /^1[0-9]{10}$/

  • 用户名验证:

    /^[a-zA-Z] [a-zA-Z0-9]{3,15}$/

  • 密码验证:

    /^[a-zA-Z0-9]{4,10}$/

  • 邮箱验证:

    /^\w+@\w+(\.[a-zA-Z]{2,3}){1,2}$/

  • 手机号验证:

    /^1\d{10}$/

  • 十六进制颜色正则:

    /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/

  • 车牌号正则:

    /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐