JS高级-一刷红宝书

JS高级-一刷红宝书

JavaScript简介

  • 文档对象模型(DOM)

    很多语言也实现了DOM,但是DOM已经成为了JavaScript中的一个重要组成部分

  • JavaScript由ECMAScript + 文档对象模型(DOM) + 浏览器对象模型(BOM)组成

  • 浏览器http请求限制 8以内
    浏览器允许的并发请求资源数是什么意思?
    页面优化——减少HTTP请求数

  • 为什么使用CDN的原因
    启用与主站域名不同的网站提供静态服务可以减少cookies传送,有的时候cookies体积很大,而每一个请求都会携带cookies往返,占用时间和带宽较大,并且能够减少服务器带宽、节约资源

在HTML中使用JavaScript

  • script标签的属性
    script之defer&async
    HTML 5 script async 属性

    async 下载后立即执行 defer 文档解析后执行

  • 外部js文件的后缀名不一定是js 但是要确保服务器返回正确的MIME类型

  • 为什么要将script标签放在页面后面
    浏览器在遇到<body>标签时才开始呈现页面内容,如果将script标签放在head里面会导致浏览器必须将全部js下载、解析、执行,js代码过多会导致页面出现明显延迟,表现为白屏,因此现代WEB应用一般将script标签放在</body>前面。

  • js代码放入外部文件
    1. 可维护性

    1. 共用js提高页面加载速度
    2. 不与浏览器解析html冲突
  • noscript标签用于在不支持js或者禁用js的浏览器上面插入内容

基本概念

  • var message这种未初始化的变量保存一个特殊的值 undefined
  • JS数据类型拥有动态性,相同的变量可以用作不同的类型。
  • 调用typeof null返回一个对象类型 特殊值null被认作是空的对象引用
  • typeof **返回的undefined可能是未初始化也可能是未声明
  • null == undefined true null === undefined false
  • number类型 IEEE754格式
  • typeof NaN number
  • 任何涉及NaN的操作都会返回NaN NaN与任何值不等 包括它自己 
  • 在chrome中 任何数值除以0都会返回 Infinity
  • 字符串创建之后不可变 赋值是重新生成空间然后销毁替换
  • 对一个对象里面的数组使用for in 遍历 可能会出问题 遍历结果和数组并不一样
  • arguments获取的是传的参数个数而不是定义时的参数个数

变量、作用域、内存问题

  • 所谓的函数参数按值传递:
    基本类型变量复制是复制的值,复制完毕不会互相影响
    引用类型变量复制的是引用地址即指针,复制完毕会互相影响
    而函数接受的参数 就是单纯的值 可能是值或指针
    但是如果重新new一个obj那么新的obj做的改变不会指向外部的对象
  • 用typeof检测基本数据类型 用instanceof检测引用数据类型
  • 一般函数内部的this指向的是调用函数的环境 但是使用new构造一个实例会改变this的指向
  • try catch和with能延长作用域链
  • 内存回收
    一般是采用标记清除的方式回收而不是采用引用计数的方式回收
  • 手动回收内存
    除了调用浏览器提供的方法 手动的一个方法是将全局变量设置为null 局部变量一般不用关心 -这种做法叫解除引用
  • 基本类型值占据大小固定空间 在栈内存中 引用类型的值是对象 在堆内存中

引用类型

  • array能通过使用push、pop、shift、unshift来实现栈和队列等
  • push方法在数组末尾添加元素
  • pop方法移除数组末尾的元素
  • shift方法移除数组开头的元素
  • unshift方法向数组开头添加元素
  • 可以用push+pop实现栈 用push+shift实现队列 unshift+pop实现反向队列
  • 数组除了操作数组的几个方法 concat、includes、indexOf、join、lastIndexOf、slice
  • 几个迭代方法 every、some、filter、find、findIndex、forEach、keys、map、reduce、reduceRight、values
  • Date类型 JSON.stringify(new Date()) 和Date.toString()不一样
  • 处理时间的库 dayjs momentjs
  • 使用var创建普通变量会引起变量提升 使用函数声明也会引起变量提升 但是使用函数表达式并不会引起变量提升
  • 函数如果没有return则返回undefined 有则返回return的值
  • 函数内部arguments对象的callee方法指向自身函数
  • arguments的caller指向调用自身函数的函数的引用
  • 函数的apply()和call()能改变this的指向 第一个参数都是传的作用域 即可以传对象等 第二个参数是参数值 apply能传数组和arguments call只能列出每一项传 不过es6有…方式进行解构赋值
  • bind方法也能绑定this的值
    - 基本数据类型是没有方法的 那么为什么能直接使用方法呢 例如 a1.substring(2)
    这是因为js做了如下操作
    当执行上述代码时 访问a1处于一种读取模式
    1. 创建String类型的一个实例
    2. 在实例上调用指定的方法
    3. 销毁这个实例
  • js 三种特殊引用类型 String Boolean Number 称作基本包装类型
  • 引用类型和基本包装类型的区别是对象的生存期问题
  • 字符串操作方法
    1. concat()连接字符串 不过现在一般用+
    2. slice substr substring
    3. indexOf lastIndexOf
    4. trim 去空格
    5. toLowerCase toUpperCase toLocalLowerCase toLocalUpperCase
    6. match 方法与正则的 exec()方法相同
    7. search replace search返回索引值
    8. split分割字符串成数组
  • 全局对象 Global
    parseInt()等方法其实都是属于Global的方法 encodeURI() decodeURI() encodeURIComponent()
  • web浏览器将Global实现在了window对象中
  • Math
    • min和max方法 一般是将很多参数当参数传入 但是也可以这么做
      1
      2
      3
      var a = [1, 2, 3, 4, 5, 6, 7, 98, 9, 0]
      var b = Math.min.apply(Math, a) //0
      var c = Math.max.apply(Math, a) //98
    • 舍入方式 Math.ceil()向上取整 Math.floor()向下取整 Math.round()标准取整
    • Math.random()随机数方法 Math.floor(Math.random()*一个整数+一个整数)
    • 遍历数组用for循环、for of forEach 尽量不用for in

面向对象的程序设计

  • 属性类型

    • 数据属性
      包含 可定义 可枚举 可读写 value值
    • 访问器属性
      包含getter setter方法 必须用defineProperty()来定义
    • 在js中 可以对任何对象 包括DOM和BOM对象 使用Object.getOwnPropertyDescriptor()方法
  • 创建对象 ★★★★★

    1. 工厂模式
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      function createPerson(name, age, job) {
      var o = new Object()
      o.name = name
      o.age = age
      o.job = job
      o.sayName = function () {
      alert(this.name)
      }
      return o
      }
      var person1 = createPerson('nick', 29, 'Software Engineer')
      var person2 = createPerson('Greg', 27, 'Doctor')
      解决创建多个相似对象的问题,却没有解决对象识别问题
    2. 构造函数模式
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      function Person(name, age, job) {
      this.name = name
      this.age = age
      this.job = job
      this.sayName = function () {
      alert(this.name)
      }
      }
      var person1 = new Person('nick', 29, 'Software Engineer')
      var person2 = new Person('Greg', 27, 'Doctor')
    • 没有显示的创建对象
    • 将属性和方法直接赋值给了this对象
    • 没有return 语句
    • 开头使用了大写P 这是想OO语言看齐
    • 必须使用new操作符创建实例 使用new操作符经历了以下过程
      • 创建一个新对象
      • 将构造函数的作用域赋给新对象 因此this就指向了这个对象
      • 执行构造函数代码 添加属性和方法
      • 返回新对象
    • 这两种创建的对象不大一样
    • 构造函数创建的对象的constructor(构造函数)属性 指向Person 最初是用来标识对象类型的 使用person1 instanceof Objcet返回true person1 instanceof Person返回true
    • 构造函数创建的每一个实例的方法都是不相等的 因为函数是对象 定义一个函数就相当于创建了一个对象
      1
      person3.sayName == person4.sayName  // false
      于是他们把共同作用的函数拿出来做成全局函数 然后每个实例的引用指向这个函数
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      function Person(name, age, job) {
      this.name = name
      this.age = age
      this.job = job
      this.sayName = sayName
      }

      function sayName() {
      alert(this.name)
      }
      然后问题又来了,首先这个全局函数只能对象用,不符合全局函数的定义,其次如果方法很多就要定义很多全局函数,那么这个类就没有封装性可言了
    1. 原型模式

      程序员真会玩

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function User() {}
      User.prototype.name = 'Mike'
      User.prototype.age = 29
      User.prototype.job = 'Software Engineer'
      User.prototype.sayName = function () {
      alert(this.name)
      }
      var user1 = new User()

      prototype 就是通过调用构造函数而创建的那个对象实例的原型对象

    • Object.getPrototypeOf()方法取得实例的原型对象
    • 对象实例的属性和方法会覆盖原型的属性和方法 但是不会修改 使用delete操作符删除实例属性并能回复对原型访问
    • 使用hasOwnProperty()方法判断属性在实例中还是在原型中,返回true代表在实例中 hasOwnProperty是从Object继承而来的
    • 为原型添加方法或属性或者修改,能够立即在所有对象实例中反应出来
    • 原型模式共享属性和方法既是优点也是缺点
    • 不能在实例化对象之后使用字面量方式重写原型 否则会切断现有实例与新原型之间的联系
    1. 组合使用构造函数模式和原型模式
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      function Person(name, age, job) {
      this.name = name
      this.age = age
      this.job = job
      this.friends = ['Shelby', 'Court']
      }
      Person.prototype = {
      constructor: Person,
      sayName: function () {
      alert(this.name)
      }
      }

      var person1 = new Person('Nicholas', 29, 'software engineer')
      var person2 = new Person('Greg', 27, 'Doctor')
      集合两种模式之长,是现在使用最广泛、认同度最高的一种创建自定义类型的方法
  1. 动态原型模式
    在构造函数中添加一个初始化原型方法的if语句
  2. 寄生构造函数模式 稳妥构造函数模式

继承

js只支持实现继承 并且主要依靠原型链来实现继承

  • 原型链继承实现
    1. 简单点来说就是定义一个father类型
    2. father类型加点原型方法
    3. 定义一个children类型 children原型 = father的实例
    4. children类型加点原型方法
    5. 结果:father类型有自己的原型方法 children类型有father类型实例的属性和方法还有它自己定义的方法
  • 注意使用对象字面量方式替换原型 很可能会导致切断联系
  • 原型链的两个问题:1 子类型的所有实例会共享父类型的引用类型值 比如数组 2 子类型创建实例的时候,不能在不影响所有对象实例的情况下向超类型的构造函数中传递参数
  • 借用构造函数
    1
    2
    3
    4
    5
    6
    7
    function SuperType() {
    this.colors = ['red', 'blue', 'yellow']
    }

    function SubType() {
    SuperType.call(this)
    }
    借用构造函数是能解决上面两个问题的,不过它也有自己的问题,那就是方法都定义在构造函数中 子类没法用超类定义的方法
  • 组合继承
    组合继承,有时候也会叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种模式。其背后思路是使用原型链实现对原型属性和方法的继承,使用借用构造函数来实现对实例属性的继承。这样既能在原型上定义方法实现了函数复用,有能够保证每个势力都有他自己的属性!
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    function SuperType(name) {
    this.name = name
    this.colors = ['red', 'blue', 'yellow']
    }

    SuperType.prototype.sayName = function () {
    console.log(this.name);
    }

    function SubType(name, age) {
    // 借用构造函数 继承属性
    SuperType.call(this, name)
    this.age = age
    }

    //继承方法
    SubType.prototype = new SuperType()
    SubType.prototype.constructor = SubType
    SubType.prototype.sayAge = function () {
    console.log(this.age);
    }

    var instance1 = new SubType('Nicholas', 29)
    instance1.colors.push('black')
    console.log(instance1.colors);
    instance1.sayName()
    instance1.sayAge()


    var instance2 = new SubType('Greg', 27)
    console.log(instance2.colors);
    instance2.sayName()
    instance2.sayAge()

    [ 'red', 'blue', 'yellow', 'black' ]
    Nicholas
    29
    [ 'red', 'blue', 'yellow' ]
    Greg
    27
  • 原型式继承
    Object.create() 以传入的对象做原型创建一个新的对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var person = {
    name: 'make',
    friends: ['a', 'b', 'c']
    }

    var person1 = Object.create(person, {
    age: {
    value: 12
    }
    })
    console.log(person1.age); //12
    console.log(person1.name); //make
    直接打印person1不显示,但是能.出来 因为属性都在原型里面。潜复制 引用指向相同的值
  • 寄生式继承
    核心是封装继承过程的函数,步骤是 调用函数创建一个新对象 增强这个对象 返回这个对象
  • 寄生组合式继承
    组合式继承虽然使用最广泛,但是他调用超类的构造函数有两次。于是程序员们使用寄生组合式继承,在SubType.prototype = new SuperType()第一次调用构造函数的这个地方更改为寄生式继承的方式 即
    1
    2
    3
    4
    5
    function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype)
    prototype.constructor = subType
    subType.prototype = prototype
    }
    相当于在继承的时候子类原型只接受了父类原型,而没有调用构造函数

    函数表达式

    函数有函数声明提升,使用函数表达式创建的函数没有声明提升

    闭包:闭包是指有权访问另一个函数作用域中的变量的函数。

最简单的闭包就是函数里面写一个函数。
调用函数发生的事:创建一个执行环境 创建一个活动对象 内部包含arguments 命名参数等 一般函数执行完毕后活动对象就被销毁 但是闭包引用了活动对象的值就不会令它简单销毁

  • 闭包与变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function a() {
    var res = new Array()

    for (var i = 0; i < 10; i++) {
    res[i] = function () {
    return i
    }
    }
    return res
    }

    var a1 = a()
    console.log(a1);

    这段代码的返回结果是 [ [Function], [Function], [Function], [Function], [Function], [Function], [Function], [Function], [Function], [Function] ] 一个函数数组,但实际上 每个函数都返回10,因为每个函数的作用域链都保存着a的活动对象,即他们引用的都是同一个i。当循环执行完之后,i的值为10,所以每个函数都返回10。
    改进版

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      function a() {
    var res = new Array()

    for (var i = 0; i < 10; i++) {
    res[i] = function (num) {
    return function () {
    return num
    }
    }
    }
    return res
    }

    var a1 = a()
    console.log(a1);

    多加了一个带参的匿名函数,由于函数参数是按值传递的,所以会将i复制给num,这样闭包返回的值都是不同的。

  • 关于this对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var name = 'The Window'
    var object = {
    name: ' My object',
    getNameFunc: function () {
    console.log(this.name);//My object
    return function () {
    return this.name //The Window
    }
    }
    }
    console.log(object.getNameFunc()());

    匿名函数的执行环境具有全局性,this通常指向window。
    改进

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    var name = 'The Window'
    var object = {
    name: ' My object',
    getNameFunc: function () {
    console.log(this.name);//My object
    var that = this
    return function () {
    return that.name //My object
    }
    }
    }
    console.log(object.getNameFunc()());
    ```

    使用that或者_this保存下this的值,亦或者使用箭头函数。
    ```js
    var name = 'The Window'
    var object = {
    name: ' My object',
    getNameFunc: function () {
    console.log(this.name);
    return () => {
    return this.name
    }
    }
    }
    console.log(object.getNameFunc()());
  • 模仿块级作用域

    1
    2
    3
    4
    5
    6
    7
    8
    (function () {
    for (var i = 0; i < 10; i++) {
    console.log(i);

    }
    })()

    console.log(i); //报错
  • 190页 单例模式

  • 现在想想 回调函数真jb骚

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function a(callback) {
    callback()
    }

    function b() {
    console.log('b');
    }

    a(b)

    主要意思就是将一个函数作为参数传给主函数,主函数执行完毕执行回调函数,这个过程主函数可以传递参数给回调函数,然后回调函数就能拿到主函数执行完毕的一些变量。
    回调函数也是闭包,我们通常使用一般不会特地声明回调函数,而是直接调用主函数然后传一个匿名参数做回调函数,回调函数拿到的参数必定是主函数执行完毕的值。也就是说使用回调函数相当于先执行完主函数。

    BOM

    window对象

  • 每个frame都有自己的window对象 可以使用window.frames[]或者top. parent.方式来访问每个frame的window对象

  • 窗口位置

    1
    2
    var leftPos = (typeof window.screenLeft === "number") ? window.screenLeft : window.screenX;
    var topPos = (typeof window.screenTop === "number") ? window.screenTop : window.screenY;

    moveTo moveBy 这种js控制浏览器的方法好多浏览器已经禁用,好像IE可能有用

  • 窗口大小
    由于浏览器之间的各个差异,因此最终虽然无法确定浏览器窗口本身大小,不过可以取得页面视口的大小

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var pageWidth = window.innerWidth;
    var pageHeight = window.innerHeight;

    if (typeof pageWidth !== "number") {
    if (document.compatMode === "CSS1Compat") {
    pageWidth = document.documentElement.clientWidth;
    pageHeight = document.documentElement.clientHeight;
    } else {
    pageWidth = document.body.clientWidth;
    pageHeight = document.body.clientHeight;
    }
    }
  • 导航和打开窗口

    1. window.open()返回一个指向新窗口的引用。调用window.close()可以关闭打开的窗口。
    2. opener属性指向打开窗口的原始窗口
  • 间歇调用和超时调用
    setTimeout 尽量传函数而不是字符串,为了避免性能的损耗。
    使用setTimeout经过指定时间代码不一定会执行。

    js是一个单线程的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个js的任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout的第二个为时间的参数是告诉js再过多长时间将当前任务添加到队列,如果队列是空的那么代码立即执行,如果队列不是空的需要等前面任务执行完了以后再执行。

    换句话说就是setTimeout指定时间之后将任务添加到队列末尾。
    不建议在开发环境使用setInterval 而是使用setTimeout模拟。

  • 系统对话框

    • confirm返回一个boolean true代表按了确认 false则是取消或者x
    • prompt 两个参数 一个是提示 一个是输入框默认值 返回用户输入或者null
    • 还有查找和打印两个异步对话框 window.find() window.print()

      location对象

      location对象既是window对象的属性,也是document对象的属性。
      location的几个属性
      hash返回url的hash、host返回服务器名称和端口号、hostname 服务器名称、href 完整url、pathname 返回url文件目录名、port 服务器端口、protocol返回页面使用协议、search 返回url查询字符串navigator对象包含了浏览器相关的信息
      screen对象包含了屏幕显示相关的信息,但是一般不敢使用screen的宽高来设置元素

      客户端检测 暂且跳过

      js判断客户端是pc端还是移动端

      DOM ★★★

      document对象是HTMLDocument的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问。
      documentElement属性指向html元素,body属性指向body元素。

      利用document取得文档信息

  • document.title 取得title标签的信息并且可以修改

  • document.URL 包含完整的URL信息

  • document.domain 取得页面的域名

  • document.referrer 取得页面的来源页

  • 以上信息都存于http头部

  • document.write 具有写入页面的能力

    Element类型

    获取到元素之后 它的属性一般直接点出来就可以 也可以通过点进行赋值。 但是自定义属性没法点出来 可以使用getAttribute()取得,还有setAttribute() removeAttribute()等方法。
    一般不使用getAttribute方式,因为还有style和onclick等方式取得的是对象。
    Text类型

  • 例如div.firstChild取得 然后使用nodeValue修改

DOM操作技术

  • 动态加载的js代码能立即执行

DOM扩展

js现在已经原生支持使用css选择符选择元素 querySelector querySelectAll

1
var div2 = document.querySelector('#divc')

与类相关的扩展

getElementsByClassName返回一个nodelist。
classList属性 返回一个元素的class数组,然后附带有remove、add
、toggle、contains(是否包含)等方法。
document.activeElement获取文档中获得焦点的元素,hasFocus()方法判断文档是否有焦点。
children获取节点的子元素 contains方法判断是否是节点的子节点。

DOM2和DOM3

变化有点看不大懂 先pass

样式

.style方式改变样式时,float改成cssFloat IE下则是styleFloat
获取样式表样式 getComputedStyle(document.getElementById('wrap')) currentStyle

  • 偏移量
    offsetHeight 元素垂直方向上的空间大小 以像素记 包括元素高度 水平滚动条高度 上下边框高度
    offsetWidth 包括元素宽度 垂直滚动条宽度 左右边框
    offsetLeft 元素左边框至包含元素左边框距离
    offsetTop 元素上边框至包含元素上边框距离
  • 客户区大小
    clientHeight 元素内容高度加上上下内边距高度
    clientWidth 元素内容宽度加上左右内边距宽度

    DOM范围 暂时略过

事件

事件分为两种:事件冒泡和事件捕获
DOM事件流:现在的DOM事件流是先捕获再冒泡
一种事件处理程序是直接在html上面使用on方式

  • 第一个缺点 由于事件绑定在元素上 因此若是函数不具备执行条件然后被触发 会导致报错
  • 第二个缺点 扩展事件处理程序的作用域链在不同浏览器中会导致不同结果
  • html和js代码耦合度高
    DOM 0级事件处理程序
  • 简单 跨浏览器 取得一个元素的引用 然后 btn.onclick = function(){} 函数里的this引用的是这个元素的属性和方法

    DOM 2级事件处理程序

    DOM2级事件处理程序使用addEventListener()removeEventListener()。所有DOM节点中都包含这两个方法,并且它们都接受三个参数:要处理的事件名,事件处理程序函数和一个布尔值。布尔值为true表示捕获阶段调用事件处理程序,false代表冒泡阶段调用事件处理程序。
    removeEventListener要移除事件,必须要是命名函数,使用匿名函数方式绑定的事件无法使用remove移除。
    不建议在捕获阶段添加事件处理程序。
    event.preventDefault()用来阻止特定事件的默认行为。
    event.stopPropagation()用来阻止元素继续冒泡。

    事件类型

    UI事件
  • DOMActivate 表示元素被用户激活 ff和chrome支持
  • load事件 页面完全加载完在window上触发 所有框架都加载完毕在框架集上触发 图像加载完毕在img元素触发 嵌入的内容加载完毕在object上触发
    怪不得要求onload写在赋值src属性之前,因为一旦设置src就会开始下载 如果是把onload写在src后面 有微小的可能性不触发onload
    而script标签则需要src和添加到文档一起设置才会开始下载
  • unload 同上 当卸载后触发
  • abort 停止下载过程中 如果嵌入内容没有加载完 在object上面触发
  • error 同load 发生错误时触发
  • select 选择文本框中的一个或者多个字符时触发
  • resize 窗口或者框架大小发生变化时 在window或者框架上面触发此方法
    触发的挺频繁
  • scroll 滚动带有滚动条的元素内容时触发
焦点事件
  • blur失去焦点时触发
  • focus获得焦点时触发 focusout冒泡
  • focusin获得焦点触发 冒泡
    鼠标与滚轮事件
    鼠标有9个事件
  • click
  • dbclick
  • mousedown 不能通过键盘触发
  • mouseenter 不冒泡 不能通过子元素触发
  • mouseleave
  • mousemove
  • mouseout 可以子元素触发
  • mouseup 松开鼠标触发
  • clientX和clientY表示鼠标 触发时的视口位置
  • pageX和pageY代表 鼠标页面位置
  • screenX和screenY表示 鼠标触发屏幕位置
  • shift ctrl alt cmd能影响鼠标事件 因此有四个布尔值表示是否按下 shiftKey ctrlKey altKey metaKey
  • mousedown和mouseup有个button属性 0鼠标左键 1鼠标滚动 2鼠标右键
  • event还有点击多少次 属性
  • offsetX offsetY距离目标元素边界值
  • 滚轮事件
  • mousewheel wheelDelta 向前滚120倍数 向后滚-120倍数
    键盘事件
  • keydown keypress keyup
  • textInput事件 输入文本框之前触发
  • charCode 键码
  • 后来变成了key和char
  • location属性 代表按下键盘那个位置 4代表虚拟键盘
    DOM变动事件
  • DOMSubtreeModified 监听DOM变动的事件
  • DOMNodeInserted 一个节点被作为子节点插入到另一个节点时触发
  • DOMNodeRemoved 节点从父节点中移除时触发
  • DOMNodeInsertedIntoDocument 节点插入文档或者通过子树插入到文档触发
  • DOMNodeRemoveFromDocument
  • DOMAttrModified 特性被修改时触发
  • DOMCharacterDataModified文本节点的值发生变化时触发
    各种页面事件还有很多啊
    触摸事件
  • touchstart touchmove touchend 多了rotate scale属性
    事件委托
    事件委托就是在最外面冒泡的元素上面进行事件程序处理,这样就相当于只挂载一次事件处理程序,然后根据target元素来判断具体响应事件。好处是节约了内存,提高了性能,对象很快就可以访问 设置事件绑定也快了很多。
    移除事件处理程序
    不用的时候移除事件处理程序也是提高性能的一种方式,不过由于现代浏览器优良的性能,笔记这附近几种方法一般采用的比较少了。占用浏览器资源较多的一个方面主要是DOM的操作,因此减少重绘重拍等方式显得还是比较重要的。
    模拟事件
    js可以模拟DOM事件,并与真的DOM事件一样使用 但是 需要传入大量的事件参数。。

    自定义事件 ★★★★★

    手写一个观察者模式的事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    function EventTarget() {
    this.handlers = []
    }
    EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function (type, handler) {
    if (typeof this.handlers[type] == "undefined") {
    this.handlers[type] = []
    }
    // 用push的意思是同一种事件可以添加多个监听方法
    this.handlers[type].push(handler)

    },
    fire: function (event) {
    if (!event.target) {
    event.target = this
    }
    // 判断事件类型有没有被绑定
    if (this.handlers[event.type] instanceof Array) {
    var handlers = this.handlers[event.type]
    for (let i = 0; i < handlers.length; i++) {
    // 循环执行绑定的方法 并传入event参数
    handlers[i](event)
    }
    }
    },
    removeHandler: function (type, handler) {
    if (this.handlers[type] instanceof Array) {
    var handlers = this.handlers[type]
    // 循环判断同事件的方法 然后删除
    for (var i = 0; i < handlers.length; i++) {
    if (handlers[i] === handler) {
    break;
    }
    }
    handlers.splice(i, 1)
    }
    }
    }

    // 使用自定义事件
    function handleMessage(event) {
    alert('Message received: ' + event.message)
    }

    var target = new EventTarget()
    target.addHandler('message', handleMessage)
    console.log(target);

    target.fire({
    type: 'message',
    message: "Hello World!"
    })
    target.removeHandler('message', handleMessage)

表单脚本

基本表单常用方法和操作 详见红宝书420页

操作剪贴板

  • beforecopy 发生复制操作前触发

  • copy 发生复制操作时触发

  • beforecut 发生剪切操作前触发

  • cut 发生剪切操作时触发

  • beforepaste 发生粘贴操作前触发

  • paste 发生粘贴操作时触发
    可以使用clipboardData对象访问剪切板数据 chrome中此对象存在于event对象中
    这个对象有三个方法 getData() setData() clearData()

    富文本 比较nb的 先过

    canvas

    getContext() 取得绘图上下文对象
    绘制矩形

    1
    2
    3
    4
    5
    6
    7
    var cas = document.getElementsByTagName('canvas')[0]
    var ctx = cas.getContext('2d')
    ctx.fillStyle = '#ff0000'
    ctx.fillRect(10, 10, 50, 50)
    ctx.fillStyle = 'rgba(0,0,255,0.5)'
    ctx.fillRect(30, 30, 50, 50)
    ctx.clearRect(40, 40, 10, 10)

    canvas能绘制圆、矩形、线 能绘制路径 设置线的样式 图形样式 能增加渐变 能旋转 缩放
    不过问题是 旋转 缩放等变换是对ctx而不是某个图形进行的操作
    getImgaeData() 取得原始图像数据 并且能对数据的元素点进行操作
    isPointInPath方法必须要用路径画 用drawimg 并不行

    webGL提供了一些操作字节级别的方法

    HTML5脚本编程

    postMessage方法能进行frame之间的通讯

    拖动

    emmm 做一个拖动好麻烦啊
    张鑫旭拖动
    赶时间 pass

    ajax XMLHttpRequest

    手写ajax实现

    var xhr = new XMLHttpRequest()
    xhr.addEventListener('readystatechange', function () {
    if (xhr.readyState === 4) {
      if (xhr.status === 200 || xhr.status === 304) {
        console.log(xhr.responseText);
    
      }
    }
    })
    var url =
    'https://route.showapi.com/341-1?maxResult=20&page=1&showapi_appid=18393&showapi_test_draft=false&showapi_timestamp=20180605172845&showapi_sign=1904d4ca2680fc3ad9e5922856e44803'
    xhr.open('get', url, true)
    xhr.send(null)
    //xhr.abort() //取消异步请求
    

    监听事件放在前面是为了兼容性。
    xhr.setRequestHeader("MyHeader","MyValue")方法修改头部信息。
    get请求除了直接地址拼接参数之外,也可以使用addURLParam()方法添加参数 url = addURLParam(url,"name","Nick")
    使用FormData提交表单数据

    var data = new FormData()
    data.append('name','nick')
    

    xhr的请求超时
    xhr.timeout = 10000
    xhr.ontimeout = function(){}
    progress事件监听请求进度
    lengthComputable 表示进度信息是否可用的布尔值
    position 表示已经接收的字节数
    totalSize表示根据content-length响应头部确定的预期字节数

    websocket 并不难

    node+html 实现websocket

    js高级技巧

    P598

Author: XavierShi
Link: https://blog.xaviershi.com/2018/05/14/JS高级-一刷红宝书/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.