陪你读书JavaScript WEB前端

269 阅读25分钟

最近在复听沙老师的陪你读书系列教程,遂决定将之前琐碎的笔记整理成文。

script 标签

script标签是向HTML插入JavaScript的主要方法。

标签的属性

  • async异步下载(只对外部脚本有效,一般不用)
  • charset字符集(大多数浏览器会忽略这个值,一般不用)
  • defer延迟执行(只对外部脚本有效,IE7及更早版本对嵌入脚本也支持这个属性,一般不用)
  • language语言(已废弃)
  • src包含代码的外部文件
  • typeMIME—— 多用途互联网邮件拓展,language的代替属性(一般不用)

标签的位置

一般情况下JavaScript代码放在body的结束标签之前,因为其是阻断式。一定要在渲染之前执行时放会放在头部,如以下两种情况:

  • Rem设置根字体的大小
  • 检测CSS属性兼容性

嵌入代码与外部文件

外部文件优点:

  • 可维护性
  • 可缓存(避免缓存方式:浏览器设置,请求文件后加随机数)
  • 适用未来

嵌入代码优点:

  • 减少连接数,提升性能

注意

  • 包含在标签内部的JavaScript代码将被从上至下依次解释

    解释包含预处理执行两个过程。

  • src属性可以跨域
  • 带有src属性的标签只会下载并执行外部脚本文件,不会执行标签之间嵌入的代码

语法

JavaScript严格区分大小写,但在HTMLCSS中使用时是不区分大小写的。

  • HTML中绑定事件时,事件名不区分大小写,但等号后面的属性值是区分大小写的
  • data-属性 “-” 后面会被强制转换成小写
  • JavaScript代码中onclick等事件名称必须全部是小写

标识符和注释

标识符指的是变量、函数、属性的名字,或函数的参数。 标识符的第一个字符必须是字母(包含任何语言,如汉字等)下划线$

    //单行注释 
    /* 
        块级注释
    */ 

语句

JavaScript解释器处理未在结尾处加分号的代码时,会先判断下一行的代码能否跟当前行一起执行,如果能执行就合并两行代码。如果不能执行就在行尾和下一行之间加分号执行,如果加分号还是不能执行就报错。

    // 不会和下面代码合并
    return 
    break
    continue   

    //会和下面一行结合       
    ++ / --
    var a = 1
    var b = 2
    a            
    ++
    b  //a:1 b:3 

数据类型

JavaScript包含NumberStringBooleanundefinednullObject(5 + 1) 种数据类型。

typeof操作符的返回值都是字符串: "undefined""boolean""string""number""object""function"

    var a = true
    var b = true
    alert(typeof a == b) //false

undefined 和 null 异同

  • 都只有一个值
  • 参与判断都返回false
  • 都没有方法
  • null是关键字,undefined不是
  • null是特殊的对象,undefined本质是windows对象的一个属性,属性的值是未定义
  • null是已经初始化的,undefined是未初始化的
  • null返回值是 "object",undefined是 "undefined"
  • 转换成数字时null返回 0,undefined返回NaN

用法

  • 当声明一个不确定是什么类型的变量时用null
  • 当用全等检测一个值是否为空时用null
  • 当用全等检测一个值是否存在时用undedined

Boolean

只有undefinednull-00NaN""转换为布尔类型时值为false,其余都为true

    Boolean(new Boolean(false))  // true

Boolean()等同于做两次非运算。

    Boolean(param) == !!param // true

Number 和 String

  • 尽量使用十进制,尽量少用十六进制,尽量不用八进制 (严格模式下无效)

  • 整数范围-253 ~ 253,开发中尽量不要超过十六位,超过会转换成科学计数法表示

  • JavaScriptCSS中小数点前可以不写数字,默认为零;小数点后面没有数字会自动转换成整数;小数点后面连续的零超过六位会转换成科学计数法表示

       .5 => 0.5
       1. 1.00 1.0 => 1
       0.0000001 => 1e-7
    
  • 0/0返回NaN, 正数/0返回Infinity, 负数/0返回-Infinity

       NaN != NaN // ture
       isNaN() // 判断传入的值是否能转换成数字。
    

数值转换

  • Number可以把任何数据类型转换为数字,结果一定为数字。
  • parseIntparseFloat用于转换字符串。

Object

与原始类型不同点:

  • 对象有属性
  • 对象有方法(特殊属性)
        var a = 'abcd'
        a.len = 4
        alert(a.len)  // undefined
        a.toUpperCase()
        alert(a) // abcd
    
  • 对象是可以改变的,原始数据类型不能改变

分类

  • 内部对象(17个)
    • 错误对象
    • 常用对象NumberStringBooleanArrayFuctionObjectDateRegExp
    • 内置对象MathJSONglobal
  • 宿主对象(平台决定)WindowDocument
  • 自定义对象

对象创建

  • 对象字面量
  • new操作符
  • Object.create(ES5)
        Object.create(param1, param2) //param1:要继承的原型,param2:对象的描述信息
    

对象属性的查询

  • 对象属性查询操作符
  • .操作符
  • []操作符

解释器遇见属性查询操作符查询对象属性时,先判断操作符前面是否是nullundefined,如果是直接报错。然后继续判断是否是对象类型,如果不是转换成对象。此时操作符如果是.直接返回属性名所对应的值。如果是括号,那么先计算括号中的值并将结果转换成字符串,然后返回该属性名所对应的值。如果属性或者属性值不存在返undefined

对象转换

原始类型 to 对象:

  • 数字
       let a = 123
       Object(a) // {[[PrimitiveValue]]: 123}
    
  • 字符串
       let a = '123'
       Object(a) // {[[PrimitiveValue]]: '123', 0: "1", 1: "2", 2: "3", length: 3, [[PrimitiveValue]]: "123"} ```
    
  • 布尔
       let a = true
       Object(a) // {[[PrimitiveValue]]: true}
    
  • nullundefined转换成对象都返回{}

对象 to 原始数据类型:

对象转换为布尔,结果一定为true

对象转成数字先调用valueOf,得不到数字再调用toString

对象转成字符串先调用toString,得不到字符串再调用valueOf

对象转成原始值先调用valueOf,得不到原始值再调用toString

  • toString返回带双引号的值
  • valueOf有原始值返回原始值,没有返回对象本身
        [1,2,3].toString() => "1,2,3" //toString在处理数组时的特殊情况
        (new Date()).valueOf() => 距离1970.1.1的毫秒数 //valueOf在处理时间对象时的特殊情况
    

注意

一个数据转换成二进制,如果前三位都是零,那么typeof的返回值为Object

表达式

  • 原始表达式
    • 常量
    • 变量

      使用var操作符定义是局部变量,反之是全局变量

      使用var操作符定义不可以被删除,反之可以删除

      使用var操作符定义变量,会发生声明提升,反之不会

    • 直接量
    • 关键字
  • 初始化表达式
  • 函数定义表达式
  • 函数调用表达式
  • 属性访问表达式
  • 对象创建表达式

操作符

优先级: 属性访问>一元操作符>乘除>加减>比较>相等>与运算>或运算>三目运算>赋值运算>逗号运算

结合性: 所有的一元操作符,三目运算符,赋值运算符,都是右结合,其余都是左结合。

一元操作符

  • +任何数据类型进行一元加操作返回结果都是数字类型
  • -任何数据类型进行一元减操相当于先进行一元加操作再取负数
  • ++
  • --
  • ~按位非
       ~10 => -10 -1  // 如果对数字进行位非运算,结果是其取负数在减去1 
    
  • !逻辑非
  • typeof
  • void
  • delete返回一个布尔值

逻辑运算符

  • 与运算

    • 两个布尔值运算时,有一个为false,返回false。两个都为true,返回true
    • 如果并且 (a==1 && b==3)
    • 如果那么,短路写法(a==1 && b=3)
  • 或运算

    • 两个布尔值运算时,有一个为true,返回true。两个都为false,返回false
    • 或者(a==1 || b==3)
    • 如果不,那么,一般用于赋值(x = a || b)

加减乘除和比较赋值

  • 加法操作符

    如果两边都是数字布尔nullundefined类型就进行数字的相加。

    如果两边有一个是字符串就进行字符串的相加。

  • 减法操作符/乘性操作符(乘法\除法\模运算)

    如果操作符两边都是数字,那么直接进行计算;如果有非数字类型,先把操作数转换成数字,然后进行运算。

    操作数有小数字类型数的时候,需要做一些处理(IEEE754浮点数问题)。

    结果一定是数字类型。

  • 关系运算符

    如果有一个是数字,另一个就转换成数字

    如果有一个数NaN,返回false

    结果一定是布尔类型

       7 > 6 > 5 //false
    
  • 相等运算符

    • === / !== (首先判断类型)

    数字:比较数值是否相等 NaN != NaN。

    布尔:比较布尔值是否相等。

    字符串:比较字符串编码是否相同。

    对象:比较对象的引用。

    • == / != (会进行隐式类型转换)

    数字、字符串、布尔混合运算时,先转换成数字,再进行比较。

    null == undefined 结果是 true。

    如果操作数存在对象,那么把对象转换成原始值再进行比较。

    以上三种情况都不符合直接返回false。

  • 赋值运算符(左侧的值叫左值,一定是一个变量或属性。尽量不要在代码中写连续赋值)

语句

语句:会促使某件事情发生的命令叫语句,一定伴有副作用。

副作用:运行语句或执行表达式对某些变量的值进行修改,删除,增加操作(++ , -- , =)。

基础语句

  • 表达式语句,表达式带有副作用(++ ,-- , = , +=,-=,*=, /= ,%=,delete,函数调用)
  • 复合语句(代码块)
  • 空语句(解释器略过不进行任何操作,常用在代码块的开头解决压缩代码导致的异常)
  • 声明语句varfunction

分支语句

  • 分支语句ifswitch

    if语句中判断条件如果是值的比较,那么用全等判断。

    switch语句中的case是全等判断。

    switch语句中的default可以写在任意位置,始终最后执行。

    switch语句中的case会从上到下依次执行,不会因为某一条件符合就中断执行,所以一般在case中加break。

循环语句

  • 循环语句whiledo whileforfor in
  • for
    • 循环判断表达式直接返回布尔类型会减少一次隐式类型转换
    • 循环表达式右侧为数组长度时,建议先定义数组长度为一个变量
  • for-in

    该循环主要用来枚举对象的属性。先对in操作符后面进行对象类型的转换,如果是null或undefined就报错(ES5不报错,变更为不执行循环体),如果是其他的原始类型转换成包装对象。然后把对象属性取出来运行in操作符前面的表达式,并将属性赋值给表达式的结果。

    • in前面的表达式可以是任意的,但结果一定是一个左值(变量,对象的属性)
    • in前面的表达式每次循环后都会被执行一次
    • in后面的表达式如果是原始类型时,数字类型和布尔类型不执行任何操作,字符串返回0 ~ array.length-1
    • 不是所有的属性都可以被枚举,如:lengthtoStringvalueOfPrimitiveValue
    • 如果一个对象继承了某个对象,继承的属性和方法也会被枚举出来
    • 如果对没有被枚举的属性进行删除,或者动态增加对象的属性,那么这些属性不会被枚举出来
    • 大部分浏览器是按照属性的定义的顺序枚举(先定义先枚举);如果属性是整数类型的(0,1,2...),那么就按照数字的顺序枚举
    • 不建议在for-in>循环中进行数组的操作

中断语句

  • continue停止当前的某一次循环或者switch分支,可以与标签语句配合使用
  • break退出当前的循环或者switch语句,可以与标签语句配合使用
  • return结束所在函数并返回操作符后在同一行的表达式
  • throw停止运行JavaScript,依次查找异常处理程序

作用域和内存

  • 声明的函数变量如果是同名,那么在当前作用域下函数会覆盖变量
  • 作用域可以嵌套,但是不可以重叠
  • evalwith可以改变作用域(破坏了解释器创建和管理作用域的规则,有性能问题)
    var a = '123'  
    a = null  // 下一次垃圾回收机制循环会对变量a进行回收。

编译过程:创建一个全局作用域 => 词法语法分析 (如果发现声明语句,如果作用域有该变量/函数则忽略,如果没有则创建变量/函数并分配一定内存空间,创建该函数的作用域) => 生成一段可运行代码。 执行过程:可运行代码在执行时会生成作用域链。

数组的使用

数组中每一项都可以保存任意类型的数据。

数组长度可以随时调整。

创建

  • new操作符
        /**
         * 只有一个数字类型参数时指的是数组的长度
         * 其他情况参数即是数组的内容
        */
        new Array(1) => [empty]
        new Array(1, 2) => [1, 2]
    
  • 对象字面量

最后一项后面不能有逗号,ie7不兼容。

不要使用多个逗号创建空数组,不同浏览器解析方式不一样。

检测

  • instanceof
       //多个iframe或多个全局环境时可能会出错
       var arr = []
       arr instanceof Array //返回true
    
  • constructor
        //多个iframe或多个全局环境时可能会出错
        var arr = []
        arr.constructor == Array //返回true
    
  • isArray
        //ES5
        var arr = []
        Array.isArray(arr) //返回true
    
  • Object.prototype.toString.call
        var arr = []
        Object.prototype.toString.call(arr) == '[object Array]' 
    

方法

  • 栈方法
        push //返回操作后数组的长度
        pop //返回本次弹出的项
    
  • 队列方法
        unshift //返回操作后数组的长度
        shift //返回本次弹出的项
    
  • 排序方法
        reverse //反转
    
        /**
         * 不传参数时先把数组的每一项转换成字符串,然后按照字符串的ASCALL码进行排序
         * 接收函数排序 sort(function(a, b) { return a - b }) 
        */
        sort
    
  • 操作方法
        /**
         * 原有数组不变
         * 默认是逗号
        */
        join
    
        /**
         * 原有数组不变
         * 一维数组会被拆分添加,二维数组里面的数组不会被拆分
        */
        concat 
    
         /**
         * 原有数组不变
         * 返回截取后新数组(包含开始位置不包含结束位置)
         * 如果是负数那么就加上数组长度
        */
        slice 
    
        splice //操作后得到的数组
    
  • 位置方法(ES5)
         /**
         * 返回都是数字
         * 匹配条件是全等
        */
        indexOf //从前往后找
        lastIndexOf //从后往前找
    
  • 迭代方法(ES5)
        /**
         * 参数:function(item, index, arr) {}
         * item 当前项
         * index 当前项索引
         * arr 数组本身
        */
        every
        some 
        filter 
        map 
        forEach
    
  • 归并方法(ES5)
        /**
         * 参数:{param1:function(pre, current, index, arr) {}, parma2:initValue}
         * pre 前一项
         * current 当前项
         * index 当前项索引
         * arr 数组本身
        */
        reduce 
        reduceRight
    

数组的去重

  • 与老数组进行对比(ES3)
  • 与老数组后面的项对比(ES3)
  • 去掉重复的(ES3)
  • 对象属性名不重复方法(ES3)
  • 先进行排序再去重(ES3)
  • indexOf(ES5)
  • Set
       let arr = []
    
       //方式1
       Array.from(new Set(arr))
    
       //方式2
       [...new Set(arr)]
    

注意

使用delete删除数组的某一项不会导致数组长度的变化,但是删除的那项的值会变成undefined

length属性总是返回正整数或者零,最大长度232 -2

时间对象

GMT格林威治(本身有一定误差,包含时区)。

UTC世界协调时间。

创建

    
    new Date() //返回系统当前时间
    new Date(1) //返回距离1970年1毫秒的时间
    new Date(2018,10,10) //返回2018年11月10号

    //返回1970年1月1号8点
    new Date(null)  
    new Date(true)
    new Date(false)

    new Date(undefined) //返回不合法时间 Invalid Date

    //参数为字符串时建议格式:月/日/年 时:分:秒
    new Date("2014-04-15") // Tue Apr 15 2014 08:00:00 GMT+0800 (中国标准时间) 包含时区信息
    new Date("2014/04/15") // Tue Apr 15 2014 00:00:00 GMT+0800 (中国标准时间)

方法

  • 继承方法
        toString //包括时区信息,不同宿主环境结果可能不一样
        toLocalString // 不包含时区信息,不同宿主环境结果可能不一样
        valueOf //时间对象的毫秒数
        now //时间对象的毫秒数,ES5
    
  • 格式化方法
        //很少应用
        toTimeString
        toDateString
        toLocalTimeString
        toLocalDateString
    
        toUTCString //相对于UTC格式,不同宿主返回字符可能不一样,一般用于设置cookie的过期时间
        toGMTStirng //相对于GMT格式,一般不用
    
  • 自身方法(33个)
        /**
         * 28个有规律的
         * 年月日时分秒毫秒
         * set
         * get 
         * UTC
         * no UTC
         * */
    
        //特殊的5个
        getTime 
        setTime
        getDay 
        getUTCDay 
        getTimezoneOffset
    

注意

  • getYear已废弃
  • 时间对象中只有天是从1开始的,月份、星期、时分秒、毫秒都是从0开始的
  • get返回值都是数字,set返回值都是时间对象
  • set方法设置数值大于最大数值时,会自动进位前面的日期(年份、月份、星期、时分秒)
        // 时间对象转换成毫秒数速度
        valueOf == getTime > Number == +(一元加) > Date.parse
    

函数的创建

  • 函数声明语句

        function funcName (param: 形参) { 函数体 } //函数体内外部都能访问函数
    
  • 函数表达式语句(匿名函数表达式语句)

        /**
         * 赋值变量
         * 对象属性值
         * 回调函数
        */
        var funcName = function (param: 形参) { 函数体 } 
    

    函数声明语句,函数会被整体提升。函数表达式语句只会提升定义的变量,函数体不会被提升。

    语句块中不要使用函数声明语句(严格模式下报错,普通模式下也会因为宿主环境的不同有不同的解析)。

  • 命名函数表达式语句

        /**
         * 函数体内部能访问函数(递归应用;调试应用)
         * ie8 bug: 赋值操作符右边的函数会像函数声明语句一样被解析,发生函数声明提升,
         * 导致函数体外面也能访问函数,造成当前作用域下的命名污染。
         * hack: 将变量名和函数名起成相同的。
         * */
        var varName = function funcName (param: 形参) { 函数体 } 
    
  • 箭头函数

        /**
         * 不会创建自己的上下文环境
         * 匿名函数没有arguements对象
         * */
        (param: 形参) => {}  
    
  • 函数生成器

        function * funcName (param: 形参) {}
    
  • 函数构造器(传参复杂需转换成字符串;解析时需调用一次eval函数;不能访问当前作用域)。

JS中的作用域

IIFE方式

    (function(){})(); 
    (function(){}()); 

    var a = function () {} (); 

    //webpack打包后代码
    true && function(){}(); 
    false || function(){}(); 
    0,function(){}(); 

    /**
     * 早期bootstrap源码
     * 一元加,一元减,位操作性能偏低,非操作性能稍微好一点 
     * */
    +function(){}() 
    -function(){}()
    ~function(){}()
    !function(){}() 

    new function () {} //不建议使用,可能会有歧义

作用

  • 避免作用域下命名污染
  • 减少作用域的查找,对整个程序性能有提升
  • 有利于压缩(名字较长的全局变量documentwindowsjquery可以在函数内简化D,W,$)
  • 保存闭包的状态(for循环中执行异步操作时,用IIFE包裹,将当前索引当做实参传进去)
  • 颠倒代码的运行顺序
      // UMD通用模块规范
      (function(funcName){funcName()})(function(){})
    

注意

括号方式的IIFE建议在前面加上分号。

with和try catch中的catch会创建块级作用域。

函数的应用

  • 函数当成值(函数表达式、对象属性值、数组对象的项)
  • 函数作为另一个函数参数
    • 操作函数,无时间差sort
    • 回调函数,有时间差AJAXNodejs文件读取等异步操作
  • 函数作为另一个函数返回值(闭包等)

闭包

函数里面嵌套函数,返回这个函数并被引用,导致垃圾回收机制不会回收该函数占用的内存,在函数作用域外部可以访问内部作用域的现象。

作用域、作用域链、执行环境和活动对象

运行一段代码(全局下):解释器会创建一个全局作用域,全局作用域下包含一个叫作用域链的列表,作用域链上包含一个叫活动对象的对象。

预处理:解释器对代码进行词/语法分析,如果遇见声明语句,先判断当前活动对象里是否包含该属性,如果有则忽略,否则就创建该属性并将其赋值为undefined。该过程结束后会生成一段可执行代码。

执行:对变量进行赋值时,会依次查看作用域链上的活动对象,如果有就进行赋值操作,如果没有就在全局活动对象上创建该属性并赋值。

包装对象

包装对象:数字、布尔和字符串类型调用方法,或进行属性访问时会先转换成对应的对象,然后再进行相应操作。这种调用的同时被创建的对象叫做包装对象。

布尔类型包装对象的方法

  • valueOf
  • toString

数字类型包装对象的方法

  • valueOf
  • toString
  • toFixed(param:保留小数点位数)
       //保留位数不要超过20位
       //保留小数时是按照四舍五入的规则进行的
    
  • toExponential(保留小数点位数) => 转换成科学计数法
  • toPrecision(所有数字的位数)

字符串类型包装对象的方法

  • valueOf
  • toString
  • charAt(获取指定位置的字符,从零开始)
  • charCodeAt(获取指定位置的字符编码,从零开始)
  • concat(字符串拼接,很少用)
  • indexOf(获取第一个匹配的字符在字符串中的位置)
  • lastIndexOf(获取最后一个匹配的字符在字符串中的位置)
  • split(对字符串通过特定“标识”进行分割)
  • slicesubstrsubstring
        /**
        * slice,substring
        * 相同点:
        * 包含起始位置,不含结束位
        * 传一个参数时,从当前参数的位置截取的字符串的最后
        * 不同点:
        * 当第二个参数小于第一个参数,substring会调换,slice不会
        * 参数有负数时,substring会将参数转换成零,slice会将参数当做倒数第几位处理
        */
        slice(param1:'开始位置',param2:'结束位置')
        substring(param1:'开始位置',param2:'结束位置')
    
        /**
        * 参数有负数时,会将参数转换成零
        */
        substr(param1:'开始位置',param2:'截取位数')
    
  • toLowerCase(转换成小写)
  • toUpperCase(转换成大写)
  • trim(去除空格)
  • trimLeft
  • trimRight
  • padStart(填充)(ES6)
  • padEnd(ES6)
  • startsWith(ES6)
  • endsWith(ES6)
  • includes(是否包含某段字符)(ES6)
  • repeat(重复几次当前字符串)(ES6)
  • search(正则)
  • match(正则)
  • replace(正则)

单体内置对象

不依赖于宿主环境,并且在JavaScript执行前就存在的对象。

不可以使用new操作符实例化。

ECMA规范的三个原则:

  • 所有浏览器相关代码都删除
  • 对象与平台无关
  • 全面支持Unicode编码

global 对象

    //属性
    undefined
    NaN
    Infinity

    //处理数字方法
    isFinite
    isNaN
    parseFloat
    parseInt

    //处理URI方法
    encodeURI //把整个URI字符进行编码
    encodeURIComponent //对部分URI字符进行编码
    decodeURI
    decodeURIComponent
    escape //只能处理ASCALL码进行处理
    unescape
    
    /**
    *低版本浏览器下将字符串转换成JSON
    *动态声明一些变量
    *一些代码压缩
    */
    eval

注意

  • eval函数声明的变量不会发生声明提升
  • 严格模式下,eval函数定义的变量,在eval外部是访问不到的
  • eval中使用的代码,无法使用调试工具调试
  • eval函数破坏了JavaScript解释器作用域规则,性能会大幅度下降
  • eval存在安全隐患,执行用户输入时要做验证

Math 对象

    //属性
    PI(π)

    round //对现在的数字加0.5后向下取整
    ceil //比当前值大的最小的整数
    floor //比当前值小的最大的整数

    /**
    * ES3下只能接受两个
    * ES5可以接受多个参数
    */    
    max 
    min

    abs //绝对值

    random //生成 0~1 之间的随机数

对象设计模式

  • 工厂模式 解决:创建多个相似对象。 问题:无法识别对象类型,constructor的值是Object
  • 构造函数模式 解决:可以识别对象类型,constructor的值是构造函数。 问题:构造函数的所有方法和属性都会在创建的对象上实例化一遍。

    new操作符:创建一个对象 => 将构造函数的作用域赋值给新的对象 => 执行函数中代码 => 返回创建对象

    工厂模式和构造模式异同:1.没有显式的创建对象 2.将所有的属性和方法赋值给this 3.没有显式的return语句

  • 原型模式 解决:占用内存问题。 问题:所有的实例都共用共同的属性和方法。
  • 组合模式(构造函数加原型模式)

方法

  • isPrototypeOf(检测对象是否是某个实例的原型)
        var object = {}
        object.prototype.isPrototypeOf(object)
    
  • hasOwnProperty(检测对象自身是否包含某个属性)
        var object = {}
        object.hasOwnProperty('property')
    
  • propertyIsEnumerable(检测对象某个属性是否可枚举)
        var object = {}
        object.propertyIsEnumerable('property')
    
  • getPrototypeOf(ES5,返回对象的原型)
        var object = {}
        Object.getPrototypeOf(object)
    
  • for in循(获取所有可枚举属性)
        var object = {}
        for(let i in object) {}
    
  • keys(获取对象所有可枚举的自有属性)
        var object = {}
        Object.keys(object)
    

注意

  • constructorES3下可枚举,在ES5下不可枚举
  • ie8下对象的属性名如果和Object上不可枚举方法同名会造该属性不可枚举

JavaScript中的继承

  • 原型链继承 优点:1.使用简单;2.在父类的原型中可以动态增加属性和方法。 缺点:1.为子类增加方法时,在放在继承之后;2.无法实现多继承;3.所有属性共享;4.无法传递参数
  • 借用构造函数继承 优点:1.可以多继承;2.解决属性共享问题;3.可以传递参数。 缺点:1.只是子类的实例,没有继承父类;2.只能继承构造函数内的属性;3.属性无法复用,浪费内存。
  • 组合式继承(伪经典模式) 缺点:1.父类的构造函数被调用两次,影响性能;2.相同属性同时存在在构造函数和原型中占内存。
  • 原形式继承
  • 寄生式继承
  • 组合寄生式继承(经典继承)

window对象

每一个框架都会包含自己的window对象,都保存在frames集合中,可以通过索引或名称的方式访问。

如果一个页面存在多个window对象,那么每一个window对象都会包含原生的构造函数,且它们之间是相互独立、互不相等的。

window对象的属性

  • top(最外层的window对象)
  • parent(当前window对象的上一层)
  • self(当前的window对象)
  • open方法
       /**
       * parma1: 要加载的url
       * parma2: 窗口目标,可以取值frame的name, _top, _self, _parent, _blank(默认值)
       * parma3: 特性字符串=>每一项使用逗号分隔,且不能包含空格;如果没有新开窗口,则此参数失效。
       * parma3: 布尔类型,是否取代浏览器历史记录。
       */
       open(parma1, parma2, parma3, parma4)
    

间歇与超时

  • setTimeout
  • setInterval
       /** setTimeout, setTimeout
       * parma1: 回调函数 | 字符串(相当于调用了一次eval函数)
       * parma2: 数字
       */
       var timerId1 = setTimeout(param1, parma2)
       var timerId2 = setInterval(param1, parma2)
    
  • clearTimeout(timerId1)
  • clearInterval(timerId2)

注意

  • 定时器是异步的
  • 使用setInterval时,H5规定的最小时间间隔是10ms,实际上在60Hz的显示器下最小时间间隔是16.7ms
  • setInterval函数具有累积效应,使用setTimeout来代替它
  • 做动画效果时,使用requestAnimationFrame代替定时器会更流畅

系统对话框

  • alert(string) 无返回值
  • confirm(string) 确认按钮返回true, 取消按钮返回false
  • prompt(string,输入框默认值) 返回用户在输入框内输入的值

注意

  • 三个对话框操作都是阻断式的
  • 对话框的样式是浏览器配置的

location对象

location对象既是window对象的属性,也是document对象的属性。

location的属性

  • hash
  • host
  • hostname
  • href
  • pathname
  • port
  • protocol
  • search

location的方法

  • replace 不会生成新的浏览记录
  • reload(params:Boolean) 是否绕过缓存

注意

  • location对象的属性都是可写的,对除了hash属性之外的属性赋值,会导致页面刷新,且会生成一条新的浏览记录

history对象

history的属性

  • go(params: 整数 | url) 前进/后退几页、
  • back
  • forward
  • length 历史浏览记录的数量

Node节点

Node属性

  • nodeType 返回 1~12 代表不同类型的节点
  • nodeName 返回一个当前节点名称的字符串
  • nodeValue 返回null或当前节点的值
  • childNodes 返回nodeList对象
  • parentNode 返回父节点
  • previousSibling 返回前一个兄弟节点
  • nextSibling 返回后一个兄弟节点
  • firstChild 返回第一个子节点
  • lastChild 返回最后一个子节点
  • ownerDocument 当前文档的文档节点

Node方法

  • hasChildNodes 返回布尔类型 => true:有子节点,false:没有子节点
  • appendChild(param: node类型节点)
  • insertBefore(param1: 要插入的节点,param2: 参照节点)
  • removeChild(param: 要删除子节点)
  • replaceChild(param1: 要插入的节点,param2: 要替换掉的节点)
  • cloneNode(parma: Boolean) 参数为ture,进行深度克隆;参数为false,克隆当前节点
  • normalize 合并文本节点

documnet节点

属性

  • documentElement 对应HTML节点
  • body 对应HTML中的body节点
  • title 对应网页的标题
  • domain 对应网页域名信息,只能从低级域名改成高级域名
  • referrer 获取前一个页面地址

element节点

属性

  • nodeType的值是1
  • nodeName的值是标签名字(大写的)
  • nodeValue的值是null
  • id 可以获取元素节点的id值
  • title 可以获取元素的title值
  • dir 取值 ltr | rtl
  • className

方法

  • getElementById
  • getElementsByName
  • getElementsByTagName
  • getElementsByClassName
  • querySelectorAll
  • querySelector
  • createElement

特性

HTML标签特有的属性,称为该标签的特性。可以用**node['attribute']**获取或设置。

非标准特性/用户自定义特性

  • getAttribute
  • setAttribute
  • hasAttribute
  • removeAttribute

使用这些方法读取/设置标准或者非标准特性,都是字符串形式。 JavaScript中的关键字,在这些方法中是可以正常使用的。

数据集属性 data-*

JavaScript中使用dataset['attribute']形式获取属性。 连字符形式的属性用驼峰形式获取属性。 dataset属性是实时双向接口。

注意

  1. 无论在HTML中属性书写的方式是大写或小写,在JavaScript中都使用小写。如果是多个单词组成的属性,那么使用驼峰规则。
  2. HTML中一些属性在JavaScript中是关键字,使用时需要进行特殊转换。
  3. JavaScript中获取的属性值可能是字符串布尔数字function样式对象
  4. 属性被设置后是无法删除的。

text节点

属性

  • nodeType的值是3
  • nodeName的值#text
  • nodeValue的值是文本本身

方法

  • appendData(param:文本)
  • deleteData(param1:位置, param2:数量)
  • insertData(param1:位置, param2:文本)
  • replaceData(param1:位置, param2:数量, param3:文本)
  • splitText(param:位置)
  • substringData(param:位置, param2:数量)
  • createTextNode(param:文本)

动态加载JavaScript脚本

  • 当使用某个脚本时,不确定会在当前页面执行
  • 不确定用户加载的是哪个脚本

动态插入的方式

  • 创建script标签,对src属性进行赋值,最后将标签插入文档中
  • 创建script标签,将JavaScript作为文本节点插入标签中,最后将标签插入文档中(ie 不支持将文本节点插入script中)
  • 创建script标签,将JavaScript作为文本赋值给text属性,最后将标签插入文档中(Safari 不支持)

加载与阻塞

如果是JavaScript脚本是直接写在HTML页面中时,脚本的加载和运行都是阻塞的。如果是动态加载的JavaScript脚本,下载脚本时不会阻塞,执行脚本会阻塞。

动态加载CSS样式表

  • 网页换肤

动态加载的方式

  • 创建link标签,设置标签的typerel属性,再对标签的href属性进行赋值,最后将标签插入到head标签中
  • 创建link标签,将CSS作为文本节点插入标签中,最后将标签插入到head标签中(ie 不支持将文本节点插入style中)
  • 创建link标签,将CSS作为文本赋值给text属性,最后将标签插入到head标签中(Safari 不支持)

注意

  • 对于加载CSS而言,如果想保持各个浏览器展示的一致性,要将创建出的标签插入head
  • 加载远程CSS需要创建link标签,加载本地CSS需要创建style标签
  • 插入页面的CSS不可以删除,会影响页面样式
  • 浏览器渲染CSS是实时执行的,如果修改了link标签的href属性会直接反应在页面上

动态和静态合集

NodeList是所有类型节点的合集。HTMLCollection是只包含元素节点的合集。 NodeList、HTMLCollection和NamedNodeMap都是动态合集。querySelector和querySelectorAll获取的NodeList是静态合集。

注意

  • NodeListHTMLCollection都是伪数组,ie中不能使用slice方式转换成数组
  • NodeListHTMLCollection都有item方法
  • 使用childNodes获取的是NodeList,使用children获取的是HTMLCollection

class和焦点管理

  • 自定义getElementsByClassName(H5)方法,因为需要遍历所有节点,所以性能差。且返回的是数组,不是HTMLCollection
  • 调用classList(H5)属性会获得一个DOMTokenList对象,该对象包含addremovecontainstoggle四个方法
  • activeElemen在文档加载期间的值是null,加载完成后会指向body,操作时指向被操作目标

判断文档是否获取焦点,使用document.hasFocus方法,可以判断用户当前是否在和网页进行交互。

文档元素获取焦点方式:

  • 文档默认加载
  • 用户操作(键盘、鼠标、触摸事件等)
  • 使用focus方法

焦点管理的意义

  • 标准的焦点管理,可以让HTML开发标准化
  • 无障碍Web应用
  • 提升用户体验

使用字符串操作DOM节点

  • innerHTML
  • outerHTML
  1. 读模式下,不同浏览器返回结果不同(标签大小写,空格,缩进等)
  2. 写模式下,设置的值和解析的dom结构可能会不一样(特殊字符转换,浏览器纠正等)
  3. 插入script标签时,标签内JavaScript代码不会执行;插入style标签时,样式会生效(ie8 之前不支持scriptstyle标签开头的字符串) 4.table标签不支持这两种属性
  • innerText
  • outerText
  1. 写模式下,赋值的字符串会被转换,但展示结果与赋的值一样
  2. innerTextouterText不是H5规范的标准
  3. outerText基本用不到
  4. 部分浏览器不兼容innerText,需要用textContent替代(火狐等)

innerText和textContent

区别:

  • textContent返回的值包含scriptstyle标签内容,innerText不包含
  • innerText返回值依赖于页面的显示,textContent依赖的是代码内容
  • innerText赋值时会触发回流操作,textContent不一定会
  • innerText赋值时值会被格式化,textContent不会
  • 文本节点nodeValue,返回结果类似textContent

自闭和元素的处理

brhr等标签设置innerHTMLinnerText时,浏览器会将自闭和标签变成闭合标签,但是插入的内容不会显示在页面上。设置表单标签时可能会影响表单元素的value值,不同浏览器效果不同。

JavaScript中的滚动操作

元素滚动

scrollIntoView,scrollIntoViewIfNeeded,scrollByLines,scrollByPages方法都是针对目标元素的。

/** 
*  H5 规范
*  兼容 ie8 以上浏览器
*  param: true => 被调用元素的顶部将和滚动区域的顶部对齐 false => 被调用元素的底部将和滚动区域的底部对齐
*  火狐浏览器可以传递一个{ behavior: 'auto|instan|smooth', block: 'start|end' } 对象作为参数
*/
scrollIntoView(param: Boolean | Object)

注意

  • scrollIntoView是基于dom节点在文档流的存在方式实现的,display属性为none的节点调用该方法没有效果
  • scrollIntoView方法对横向和纵向滚动都生效
  • 本身在视窗中且不再顶部或底部的元素调用scrollIntoView方法,浏览器依然会发生滚动
  • 本身在视窗中的元素调用scrollIntoViewIfNeeded方法,浏览器不会发生滚动

页面滚动

定点滚动:向某一个位置或某一个方向滚动。scrollTo,scrollBy方法是window对象的方法。

/** 
*  *param1: 滚动到页面横轴方向的X处
*  *param2: 滚动到页面纵轴方向的Y处
*/
scrollTo(param1: Number, param2: Number)

/** 
*  *param1: true => 像横轴正或反方向滚动 X px
*  *param2: true => 像纵轴正或反方向滚动 Y px
*/
scrollBy(param1: Number, param2: Number)

滚动属性

scrollTop属性可以获取或设置元素的内容的垂直滚动像素,基于节点在文档流的存在方式实现的。 scrollLeft属性可以获取或设置元素的内容的水平滚动像素,基于节点在文档流的存在方式实现的。

//获取浏览器滚动条距离兼容写法
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
var scrollLeft = document.documentElement.scrollLeft || window.pageYOffset || document.body.scrollLeft

scrollHeigthscrollWidth是只读属性。

  • 返回的高度或宽度不包括滚动条的宽度
  • 返回的高度或宽度包含padding,不包含margin
  • 修改盒模型的box-sizing属性会对取值有影响
  • 不同浏览器取值会有差异,一般参与运算要有容错区间

脚本化CSS

在HTML使用样式表的方式有外部样式表、嵌入式样式表和内联样式表三种方式。

内联样式表

通过dom节点的style属性获取的CSSStyleDeclaration样式对象,只包含内联样式表所对应的样式信息。

注意

  • CSS属性名包含连字符时,需要将属性名改成驼峰命名
  • CSS属性名为JavaScript保留字时,需要在属性前加前缀(float -> cssFloat)
  • 所有CSS的属性都是可读可写
  • 如果设置的属性不符合规范,浏览器不会报错,只是不执行
  • 混合模式下可以不设置度量单位(不建议)
  • 如果在JavaScript中读取一个没有设置过行内的样式,返回一个空字符串
  • 不同浏览器下,JavaScript读取的颜色值格式不同(rgb,hex)

style对象的属性和方法

    /**
     * ie8 下所有属性名称是大写的
    */
    cssText
    
    /**
     * 返回内联样式的个数
    */
    length

    /**
     * 返回CSSRule对象
    */
    parentRule

    /**
     * ie8 及以下不支持
     * 返回 'important' 或 ''
    */
    getPropertyPriority()

    /**
     * ie8 及以下不支持
     * 返回属性值
    */
    getPropertyValue()

    /**
     * ie8 及以下不支持
     * 返回指定位置的CSS属性
    */
    item()

    /**
     * ie8 及以下不支持
     * 移除指定样式
     * 返回被移除样式属性的属性值
    */
    removeProperty()

    /**
     * ie8 及以下不支持
     * propertyName: 属性名
     * 属性值: 属性值
     * priority: 'important'|null
    */
    setProperty(propertyName,propertyValue,priority)

    /**
     * ie8 及以下不支持
     * 返回一个CSSRule对象
    */
    getPropertyCSSValue()

计算样式

    /**
     * ie8 及以下不支持
     * element: 获取计算样式的 dom 节点
     * pseudoElt: 要匹配的伪元素的字符串
     * 返回一个 CSSStyleDeclaration 对象,包含的是当前元素计算后的样式
    */
    window.getComputedStyle(element, [pseudoElt])
    getComputedStyle(element, [pseudoElt])
    document.defaultView.getComputedStyle(element, [pseudoElt])

    /**
     * 返回一个 CSSStyleDeclaration 对象,包含的是当前元素计算后的样式
    */
    currentStyle

注意

  • getComputedStyle是方法,currentStyle是属性
  • getComputedStyle获取的计算样式是只读的
  • getComputedStyle读取复合属性时有兼容问题,要通过精确属性获取
  • getComputedStyle获取计算样式时,浏览器先通过优先级获取样式表中的样式,然后对值进行计算(样式相互影响),最后进行布局计算(百分比,rem等转换成像素单位)
  • getComputedStyle元素没有设置绝对定位的时候,通过计算属性获取top等属性返回auto字符串
  • getComputedStyle计算样式有不确定性(font-family返回的是一个字符串不会明确当前使用的是那种字体)
  • currentStyle获取的属性值可能会包含百分比

内部和外部样式表

获取StyleSheet对象的方式

  • document.styleSheets 返回一个由StyleSheet对象组成的StyleSheetList(伪数组)
  • 获取style或者link标签的dom节点,然后获取节点上sheet属性(ie 下是styleSheet)

StyleSheet对象的属性

  • cssRules 包含当前层叠样式表的属性合集
  • disabled 控制当前层叠样式表是否生效
  • href
  • media 返回一个mediaList
  • ownerNode 当前层叠样式表对象所在节点
  • ownerRule 当前层叠样式表是否使用import引入
  • title
  • type

CSSStyleRule对象的属性

  • cssText> 当前样式规则完整文本
  • selectorText 当前样式选择器
  • style 返回当前规则的CSSStyleDeclaration(伪数组)
  • parentRule 当前规则是否是import导入的
  • parentStyleSheet 返回CSSStyleSheet对象
  • type

CSSStyleDeclaration对象

脚本化CSS类

    /**
     * classList 是一个伪数组(DOMTokenList对象)
     * HTML5 包含以下四个方法
    */
    add(class1, class2, ...)
    remove(class1, class2, ...)
    toggle(class, true|false)
    contains(class)
    replace(previousClass, currentClass)

注意

  • className属性只能赋值字符串类型属性值,否则会转换成字符串进行赋值
  • 会替换原有的className的属性值

事件

事件是发生于dom元素、document对象或者window对象有关的预定义或自定义的时刻。

事件触发的方式

  • HTML内联属性
  • 属性事件(dom0级)
  • 事件监听回调(dom2级)

事件的使用

HTML事件的使用

HTML中每个元素支持的每种事件的都可以用对应的HTML标签来指定事件,通常使用on加事件名称绑定,后面跟上需要执行的JavaScript代码。

HTML事件的注意

  • HTML事件尽量不要使用特殊符号,如果一定要用需要转义
  • HTML事件执行的JavaScript的代码是在全局作用域下执行的
  • HTML事件在创建时,对应的函数内部会封装一个局部变量event,this指向触发事件的dom元素
  • 可以使用setAttributeHTML事件内部代码进行修改

HTML事件的缺陷

  • 时间差
  • 代码耦合度
  • 全局作用域下,不同浏览器的解析规则可能会造成差异

属性事件的使用(dom0)

添加事件: 获取节点,然后绑定事件属性,赋值事件处理函数。对应的函数内部会封装一个局部变量event,this指向触发事件的元素。

删除事件:获取节点,然后绑定事件属性,将处理函数设置为null。

属性事件的缺陷

  • 一次只能绑定一个事件处理函数

事件监听回调的使用(dom2)

dom2级事件包含事件捕获,事件目标,事件冒泡三个阶段。规范规定捕获阶段不涉及目标。

    /**
     * param1:事件名称,不包含on
     * param2: 事件处理函数
     * param3: 事件执行阶段,可以传入一个Boolean值,或{capture: Boolean, once: Boolean, passive: Boolean}对象 
     * 
     * 注意:
     * 事件处理函数中的 this 指向 dom元素
     * passive 主要是为了解决滚动,触摸等连续触发的耗费性能的事件
     * 同一个节点增加多个同一类型的事件监听,有一个没有指定passive,就不会对当前节点优化
     * 同一个节点增加多个不同类型的事件监听,后面的会失效
    */
    addEventListener(param1, param2, param3)

    /**
     * param1:事件名称,不包含on
     * param2: 事件处理函数
     * param3: 事件执行阶段,只需传入一个Boolean值
     * 
     * 注意:
     * 一次只能删除冒泡或捕获阶段的一个事件处理程序
     * 删除的和添加的函数必须指向同一个引用
    */
    removeEventListener(param1, param2, param3)

检查浏览器是否支持passive属性

    var isPassive

    try {
        Object.defineProperty({}, 'passive', {
            get: function () {
                isPassive = true
                window.addEventLister('abc', null, isPassive)
            }
        })
    } catch (e) {
        //
    }

    /**
     * 低版本ie下的dom2级事件
     * param1:事件名称
     * param2: 事件处理函数
     * 
     * 注意:
     * 时间处理函数在冒泡阶段执行
     * 事件处理函数中的 this 指向 window对象
     * 事件处理函数是按照书写相反的顺序执行
     * 删除事件时,传入的函数需要与绑定事件的函数必须为同一引用
    */
    attachevent(param1, param2)
    detachEvent(param1, param2)

事件优先级

事件发生后,事件首先会从window对象向目标元素进行捕获,在捕获的过程中依次触发addEventListener设置为捕获的事件,进入到目标元素及冒泡阶段是按照事件书写的顺序依次执行。

注意

  • CSS的改变不会改变事件的传播
  • 移动端的事件穿透

事件对象

常用属性

  • bubbles 返回一个布尔值表示是否是冒泡
  • cancelable 返回是否可以取消当前事件的默认行为
  • currentTarget 返回当前处理程序正在处理的元素
  • eventPhase 返回事件传播的当前阶段(1.捕获 2.目标 3.冒泡)
  • target 返回触发此事件的元素
  • type 返回当前 Event 对象表示的事件的名称

常用方法

  • preventDefault
  • stopPropagation
  • stopImmediatePropagation

鼠标事件

  • click 单击事件
  • dblclick 双击事件
  • contextmenu 右键事件
  • mousedown 按下鼠标键时触发
  • mouseup 释放按下的鼠标键时触发
  • mousemove 鼠标移动事件
  • mouseover 移入事件
  • mouseout 移出事件
  • mouseenter 移入事件
  • mouseleave 移出事件
  • mousewheel 滚轮事件,DOMMouseScroll(火狐)
  • select

鼠标事件对象属性

  • screenX/screenY
  • clientX/clientY
  • pageX/pageY(ie9以下不支持)
  • layerX/layerY(ie9以下不支持,以border为参考点)
  • offsetX/offsetY(以content为参考点)
  • movementX/movementY
  • ctrlKey
  • shiftKey
  • altKey
  • metaKey
  • button -1没按键,0主键,1辅助键,2副键

注意

  • relatedTarget表示一个与事件相关的次要焦点。存在于focusinfocusoutmouseovermouseoutmouseentermouseleavedragenter~~dragexit~~的事件对象中
  • mousewheel事件对象上有一个wheelDelta属性,滚动条向下滚时为负数值,向上滚动时是正数值,值的大小通常是120的倍数。火狐浏览器DOMMouseScroll事件对象上有一个detail属性,滚动条向上滚动是负数值,向下是正数值。HTML5实现了标准的wheel事件对象有上deltaXdeltaYdeltaZ等属性
  • 鼠标滚动事件与页面滚动无关,只能通过鼠标滚轮、触控板或触控设备的缩放触发
  • 鼠标滚动事件的触发与页面是否滚动到顶部和底部无关

参考资料

JavaScript高级程序设计(第三版)