js逆向--一点前端知识

浏览器

组件和解释器

五个组件:

  • 用户界面 : 显示操作界面
  • 浏览器引擎: 负责将用户的操作传递给对应的渲染引擎
  • 渲染引擎: 使用三个解释器去解释对应的文档中的代码。然后根据解释器的结果重新排版页面
  • 数据存储: 在本地存储一些体积较小的数据,如 Cookies、Storage 对象等
  • 网络: 它会自动加载 HTML 文档中所需要的其他资源。

三个解释器:

  • HTML解释器
  • JavaScript解释器
  • CSS解释器

HTML

JavaScript知识

  • JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。

类型

六种基本数据类型

  • 数值(number):整数和小数(比如1和3.14)。

    • JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数。
    • 由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。
    • Infinity
      • Infinity表示“无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity。
      • 由于数值正向溢出(overflow)、负向溢出(underflow)和被0除,JavaScript 都不报错,所以单纯的数学运算几乎没有可能抛出错误。
      • Infinity大于一切数值(除了NaN),-Infinity小于一切数值(除了NaN)。
      • Infinity与NaN比较,总是返回false。
  • 字符串(string):文本(比如Hello World)。

    • 字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)。
    • 但是,字符串与数组的相似性仅此而已。实际上,无法改变字符串之中的单个字符。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)。

    • 如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。
      • undefined
      • null
      • false
      • 0
      • NaN
        • 表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。
        • NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚。
        • NaN不等于任何值,包括它本身。
        • NaN与任何数(包括它自己)的运算,得到的都是NaN。
      • ""或''(空字符串)
      • 注意,空数组([])和空对象({})对应的布尔值,都是true。
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值。

  • null:表示空值,即此处的值为空。

  • 对象(object):各种值组成的集合。

对象

  • 查看一个对象本身的所有属性,可以使用Object.keys方法。
  • 删除属性
    • delete命令用于删除对象的属性,删除成功后返回true
    • 删除一个不存在的属性,delete不报错,而且返回true
    • 只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。(定义属性不能删除,如defineProperty)
    • delete命令只能删除对象本身的属性,无法删除继承的属性
  • 判断属性是否存在
    • in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。
    • hasOwnProperty:方法判断一下,是否为对象自身的属性。
  • 遍历属性
    • for...in循环用来遍历一个对象的全部属性。
      • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
      • 它不仅遍历对象自身的属性,还遍历继承的属性
    • 只想遍历对象自身的属性,应该结合使用hasOwnProperty方法

Object

  • JavaScript 的所有其他对象都继承自Object对象,即对象都是Object的实例。

  • Object对象的原生方法分成两类:Object本身的方法与Object的实例方法。

    • Object对象本身的方法:直接定义在Object对象的方法
    • Object的实例方法:所谓实例方法就是定义在Object原型对象Object.prototype上的方法。它可以被Object实例直接使用。
  • 构造函数

    • 典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。
    • 构造函数就是一个普通的函数,new命令的作用,就是执行构造函数,返回一个实例对象。
          var obj= function () {
              this.price= 1000;
          };
          var v = new Vehicle();
          v.price // 1000
      
      
  • 静态方法

    • 所谓“静态方法”,是指部署在Object对象自身的方法
    • Object.keys() 遍历对象的属性。返回可枚举的属性名
    • Object.getOwnPropertyNames() 遍历对象的属性。可返回不可枚举的属性名
    • Object.getOwnPropertyDescriptors() 获取对象所有属性的描述对象。
    • Object.defineProperty():通过描述对象,定义某个属性。
    • Object.defineProperties():通过描述对象,定义多个属性。
      • 可以重写get set,来进行hook
            var obj = Object.defineProperty({}, 'p', {
                get: function () {
                    return 'getter';
                },
                set: function (value) {
                    console.log('setter: ' + value);
                }
            });
        
            obj.p // "getter"
            obj.p = 123 // "setter: 123"
        
        
      • 监听cookie
            var cookie = document.cookie;
            document = Object.defineProperty(document, 'cookie', {
                get: function () {
                    console.log('getter: ' + cookie);
                    return cookie;
                },
                set: function (value) {
                    console.log('setter: ' + value);
                    cookie = value
                }
            });
        
        
    • Object.getPrototypeOf方法返回参数对象的原型 (检测点)
    • Object.setPrototypeOf() 方法为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。
  • Object 的实例方法

    • Object.prototype.valueOf() 返回当前对象对应的值。
      • 对象转字符串:.valueOf | .toString 重写方法 类比java 的object toString
    • Object.prototype.toString() 返回当前对象对应的字符串形式。
      • 由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
    • Object.prototype.toLocaleString() 返回当前对象对应的本地字符串形式。
    • Object.prototype.hasOwnProperty() 判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
    • Object.prototype.isPrototypeOf() 判断当前对象是否为另一个对象的原型。
    • Object.prototype.propertyIsEnumerable():判断某个属性是否可枚举。
  • 创建对象的方式

    • 直接赋值,var obj = {},var obj = Object({})(转为对象)
    • new关键字创建,var obj = new Object();
    • Object.create:构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。
    • 区别:
      • 字面量和new关键字创建的对象是Object的实例,原型指向Object.prototype,继承内置对象Object
      • Object.create(arg, pro)创建的对象的原型取决于arg,arg为null,新对象是空对象,没有原型,不继承任何对象;arg为指定对象,新对象的原型指向指定对象,继承指定对象
  • 对象的拷贝

    • 如果要拷贝一个对象,需要做到下面两件事情。
      • 确保拷贝后的对象,与原对象具有同样的原型。
      • 确保拷贝后的对象,与原对象具有同样的实例属性。
          function copyObject(orig) {
              return Object.create(
                  Object.getPrototypeOf(orig),
                  Object.getOwnPropertyDescriptors(orig)
              );
          }
      
      
  • 原型对象object.prototype

    • JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。
    • JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。每个对象都有一个__proto__属性,指向原型。
    • 原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
    • 当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。这就是原型对象的特殊之处。
  • 原型链

    • JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……
  • constructor

    • prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
          function P() {}
          P.prototype.constructor === P // true
          检测环境点:
          document.constructor === HTMLDocument
          navigator.constructor === Navigator
      
    • constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
    • 有了constructor属性,就可以从一个实例对象新建另一个实例。
          function Constr() {}
          var x = new Constr();
          var y = new x.constructor();
          y instanceof Constr // true
      
  • 包装对象

    • 对象是 JavaScript 语言最主要的数据类型,三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”
    • Boolean() , String() , Number()
    • 使用new 则为创建包装对象,不使用为强转
  • this关键字

  • this关键字是一个非常重要的语法点。毫不夸张地说,不理解它的含义,大部分开发任务都无法完成。

  • 简单说,this就是属性或方法“当前”所在的对象。

  • this主要有以下几个使用场合。

    • 全局环境使用this,它指的就是顶层对象window。
    • 构造函数中的this,指的是实例对象。
    • 如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
  • 绑定this的方法

    • this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。
    • JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
    • Function.prototype.call(thisValue, arg1, arg2, ...)
      • 函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
    • Function.prototype.apply(thisValue, [arg1, arg2, ...])
      • apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。
    • Function.prototype.bind(thisValue, arg1, arg2, ...)
      • 用于将函数体内的this绑定到某个对象,然后返回一个新函数。
    • 示例:
          var d = new Date();
          d.getTime() // 1481869925657
      
          var print = d.getTime;
          print() // Uncaught TypeError: this is not a Date object.  这样在全局作用域内调用,函数里面的this指向了window对象
      
          var print = d.getTime.bind(d);
          print() // 1481869925657
      
      

函数

  • 函数的3种声明方式

    • function 命令
    • 函数表达式
    • Function 构造函数
      • 你可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。
      • Function构造函数可以不使用new命令,返回结果完全一样
      • 总的来说,这种声明函数的方式非常不直观,几乎无人使用(除了混淆)
  • 立即调用的函数表达式

    • 根据 JavaScript 的语法,圆括号()跟在函数名之后,表示调用该函数。
      • var f = function f(){ return 1}();
      • (function(){ /* code */ }());
      • (function(){ /* code */ })();
      • 分号都是必须的
    • 通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:
      • 一是不必为函数命名,避免了污染全局变量;
      • 二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
  • 属性和方法

    • name 属性 返回函数的名字
    • length 属性 返回函数预期传入的参数个数
    • toString() 返回一个字符串,内容是函数的源码
    • 通过断点调试可以查看到
  • 作用域

    • 作用域(scope)指的是变量存在的范围。
      • 在 ES5 的规范中,JavaScript 只有两种作用域:
      • 一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;
      • 另一种是函数作用域,变量只在函数内部存在。
      • ES6 又新增了块级作用域。
    • 函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
    • 在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。
    • 局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
    • 函数本身的作用域就是其声明时所在的作用域,与其运行时所在的作用域无关。
    • 函数体内部声明的函数,作用域绑定函数体内部(闭包)
  • 变量名提升

    • 与全局作用域一样,函数作用域内部也会产生“变量提升”现象。
    • var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
  • 参数的省略

    • 函数参数不是必需的,JavaScript 允许省略参数。
    • 函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。
  • 传参

    • 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。
      • 这意味着,在函数体内修改参数值,不会影响到函数外部。
    • 如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。
      • 也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
  • arguments 对象

    • 由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
    • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
    • 通过arguments对象的length属性,可以判断函数调用时到底带几个参数。
    • 如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组
      • Array.prototype.slice.call(arguments);
      • 遍历push进数组
    • arguments对象带有一个callee属性,返回它所对应的原函数。
  • 闭包

    • 是 JavaScript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
    • 理解闭包,首先必须理解变量作用域。前面提到,JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。
    • JavaScript 语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
    • 理解闭包
      • 闭包简单理解成“定义在一个函数内部的函数”。
      • 闭包最大的特点,就是它可以“记住”诞生的环境,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
      • 闭包的最大用处有两个,一个是可以读取外层函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。

数组

  • 数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
  • 任何类型的数据,都可以放入数组。
  • 本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。
  • Object.keys方法返回数组的所有键名
  • 属性
    • length 属性
      -length属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员数量会自动减少到length设置的值。
      -清空数组的一个有效方法,就是将length属性设为0
    • 由于数组本质上是一种对象,所以可以为数组添加属性
  • 遍历
    • for...in 循环
    • for循环
    • while循环
    • forEach
  • 空位
    • 遍历时,空位都会被跳过
    • 某个位置是undefined,遍历的时候就不会被跳过
    • 空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。
  • 类似数值的对象
    • 如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”
    • 典型的“类似数组的对象”是函数的arguments对象,以及大多数 DOM 元素集,还有字符串。
    • 数组的slice方法可以将“类似数组的对象”变成真正的数组。
      • var arr = Array.prototype.slice.call(arrayLike);
    • 除了转为真正的数组,“类似数组的对象”还有一个办法可以使用数组的方法,就是通过call()把数组的方法放到对象上面。
      • Array.prototype.forEach.call(arrayLike, function);
  • 常用方法
    • isArray() 方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足。
    • push(),pop()
      • push: 数组的末端添加一个或多个元素、并返回添加新元素后的数组长度
      • pop:方法用于删除数组的最后一个元素,并返回该元素。
    • shift(),unshift()
      • shift:用于删除数组的第一个元素,并返回该元素
      • unshift: 用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度
    • join() 以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。
    • concat() 用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变
    • reverse() 用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。
    • slice() 提取目标数组的一部分,返回一个新数组,原数组不变。
    • splice() 删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。
    • sort() 对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变
    • map() 将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
    • forEach() 方法与map()方法很相似,不返回值,只用来操作数据
    • filter() 用于过滤数组成员,满足条件的成员组成一个新数组返回。
    • indexOf()返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

Math对象

  • Math是 JavaScript 的原生对象,提供各种数学功能。该对象不是构造函数,不能生成实例,所有的属性和方法都必须在Math对象上调用。
  • Math.abs()
  • Math.max(),Math.min()
  • Math.floor(),Math.ceil()
  • Math.round()
  • Math.pow()
  • Math.sqrt()
  • Math.log()
  • Math.exp()
  • Math.random()
  • 三角函数方法

Date对象

  • Date对象是 JavaScript 原生的时间库。它以国际标准时间(UTC)1970年1月1日00:00:00作为时间的零点,可以表示的时间范围是前后各1亿天(单位为毫秒)。
  • 静态方法
    • Date.now()
    • Date.parse()
    • Date.UTC()
  • 实例方法
    • Date.prototype.valueOf()
    • Date.prototype.toString()
    • Date.prototype.toUTCString()
    • Date.prototype.toISOString()
    • Date.prototype.toJSON()

Json对象

  • JSON 格式(JavaScript Object Notation 的缩写)是一种用于数据交换的文本格式,2001年由 Douglas Crockford 提出,目的是取代繁琐笨重的 XML 格式。
  • 静态方法
    • JSON.stringify()
    • JSON.parse()

console对象

  • console.log(),console.info(),console.debug(),console.warn(),console.error() 不同日志等级的打印输出
  • console.table() 用表格形式打印一些复合类型数据 如对象、数组
  • console.count() 计数输出多少次
  • console.dir(),console.dirxml() 打印一个dom的对象形式 、目录树的形式打印dom
  • console.assert() 主要用于程序运行过程中,进行条件判断,如果不满足条件,就显示一个错误,但不会中断程序执行。这样就相当于提示用户,内部状态不正确。
  • console.time(),console.timeEnd() 用于计时,可以算出一个操作所花费的准确时间
  • console.group(),console.groupEnd(),console.groupCollapsed() 这些方法用于将显示的信息分组。它只在输出大量信息时有用
  • console.trace(),console.clear() 显示当前执行的代码在堆栈中的调用路径、清空控制台输出。

类型判断

  • typeof: 返回一个值的数据类型。 使用 new 操作符创建变量,除 Function 外的所有构造函数的类型都是 'object'
  • instanceof:表示对象是否为某个构造函数的实例。检测 constructor.prototype 是否存在于参数 object 的原型链上
  • isPrototypeOf: 方法用于测试一个对象是否存在于另一个对象的原型链上。(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf)
  • Object.prototype.toString.call(obj) 返回对象obj的全类型,比如'123',返回的是'[object String]'
  • 数组类型
    • typeof [] 返回 'object'
    • Object.prototype.toString.call([]) 返回 '[object Array]'
    • Array.isArray([]) 返回 true
  • 来自MDN获取对象类型的函数
        function type(obj, fullClass) {
    
            // get toPrototypeString() of obj (handles all types)
            // Early JS environments return '[object Object]' for null, so it's best to directly check for it.
            if (fullClass) {
                return (obj === null) ? '[object Null]' : Object.prototype.toString.call(obj);
            }
            if (obj == null) { return (obj + '').toLowerCase(); } // implicit toString() conversion
    
            var deepType = Object.prototype.toString.call(obj).slice(8,-1).toLowerCase();
            if (deepType === 'generatorfunction') { return 'function' }
    
            // Prevent overspecificity (for example, [object HTMLDivElement], etc).
            // Account for functionish Regexp (Android <=2.3), functionish <object> element (Chrome <=57, Firefox <=52), etc.
            // String.prototype.match is universally supported.
    
            return deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/) ? deepType :
            (typeof obj === 'object' || typeof obj === 'function') ? 'object' : typeof obj;
        }
    

全局方法

数值相关

  • parseInt() 方法用于将字符串转为整数。
    • 对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果。
  • parseInt方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数。默认情况下,parseInt的第二个参数为10,即默认是十进制转十进制
  • parseFloat() 方法用于将一个字符串转为浮点数。
  • isNaN方法可以用来判断一个值是否为NaN
    • 只对数值有效,如果传入其他值,会被先转成数值。
    • 判断NaN更可靠的方法是,利用NaN为唯一不等于自身的值的这个特点,进行判断(value !== value)
  • isFinite方法返回一个布尔值,表示某个值是否为正常的数值。

Base64 转码

  • 浏览器
    • btoa():任意值转为 Base64 编码
    • atob():Base64 编码转为原来的值
    • 非ASCII码要转码再base64 encodeURIComponent、decodeURIComponent
  • NodeJs
    • var b64encode = new Buffer('JavaScript').toString('base64');
    • var b64decode = new Buffer(b64encode,'base64').toString();

eval

  • eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。
  • eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。
  • eval的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,一般不推荐使用。
  • eval的别名调用
        var m = eval;
        m('var x = 1');
    
        为了保证eval的别名不影响代码优化,JavaScript 的标准规定,凡是使用别名执行eval,eval内部一律是全局作用域。
    
        eval.call(null, '...')
        window.eval('...')
        (1, eval)('...')
        (eval, eval)('...')
    
    

DOM

概念

  • DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
  • 浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。
  • DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言。后面介绍 JavaScript 对 DOM 标准的实现和用法。
  • 节点
    • DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
    • 节点的类型有七种。
      • Document:整个文档树的顶层节点
      • DocumentType:doctype标签(比如)
      • Element:网页的各种HTML标签(比如、<a>等)
      • Attr:网页元素的属性(比如class="right")
      • Text:标签之间或标签包含的文本
      • Comment:注释
      • DocumentFragment:文档的片段
      • 浏览器提供一个原生的节点对象Node,上面这七种节点都继承了Node,因此具有一些共同的属性和方法。
  • 节点树
    • 一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,又像一棵树。
    • 浏览器原生提供document节点,代表整个文档。
    • 文档的第一层有两个节点,第一个是文档类型节点(<!doctype html>),第二个是 HTML 网页的顶层容器标签。后者构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。
    • 除了根节点,其他节点都有三种层级关系。
      • 父节点关系(parentNode):直接的那个上级节点
      • 子节点关系(childNodes):直接的下级节点
      • 同级节点关系(sibling):拥有同一个父节点的节点
      • DOM 提供操作接口,用来获取这三种关系的节点。比如,子节点接口包括firstChild(第一个子节点)和lastChild(最后一个子节点)等属性,同级节点接口包括nextSibling(紧邻在后的那个同级节点)和previousSibling(紧邻在前的那个同级节点)属性。
  • Document节点
    • document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。
    • document对象有不同的办法可以获取。
      • 正常的网页,直接使用document或window.document。
      • iframe框架里面的网页,使用iframe节点的contentDocument属性。
      • Ajax 操作返回的文档,使用XMLHttpRequest对象的responseXML属性。
      • 内部节点的ownerDocument属性。
      • document对象继承了EventTarget接口和Node接口,并且混入(mixin)了ParentNode接口。这意味着,这些接口的方法都可以在document对象上调用。除此之外,document对象还有很多自己的属性和方法。

事件

  • 事件的本质是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现。DOM 支持大量的事件
  • DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。
  • 该接口主要提供三个实例方法
    • addEventListener(type, listener[, useCapture]):绑定事件的监听函数
    • removeEventListener(type, listener[, useCapture]):移除事件的监听函数
    • dispatchEvent(event):触发事件
  • 监听函数
    • 浏览器的事件模型,就是通过监听函数(listener)对事件做出反应。事件发生后,浏览器监听到了这个事件,就会执行对应的监听函数。这是事件驱动编程模式(event-driven)的主要编程方式。
    • 三种方法为事件绑定监听函数。
      • HTML 的 on- 属性
            <body onload="doSomething()">
            <div onclick="console.log('触发事件')">
        
      • 元素节点的事件属性
        • xxx.onload = doSomething;
      • EventTarget.addEventListener()
        • 所有 DOM 节点实例都有addEventListener方法,用来为该节点定义事件的监听函数。
  • 事件的传播
    • 一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
      • 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
      • 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
      • 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
      • 这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
  • Event 对象
    • 事件发生以后,会产生一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。
    • Event对象本身就是一个构造函数,可以用来生成新的实例。
      • event = new Event(type, options);
    • Event构造函数接受两个参数:
      • 第一个参数type是字符串,表示事件的名称;
      • 第二个参数options是一个对象,表示事件对象的配置。该对象主要有下面两个属性:
        • bubbles:布尔值,可选,默认为false,表示事件对象是否冒泡。
        • cancelable:布尔值,可选,默认为false,表示事件是否可以被取消,即能否用Event.preventDefault()取消这个事件。一旦事件被取消,就好像从来没有发生过,不会触发浏览器对该事件的默认行为。

浏览器环境

  • window对象

    • 浏览器里面,window对象(注意,w为小写)指当前的浏览器窗口。它也是当前页面的顶层对象,即最高一层的对象,所有其他对象都是它的下属。一个变量如果未声明,那么默认就是顶层对象的属性。
  • Navigator 对象

    • 一个包含浏览器和系统信息的 Navigator 对象。脚本通过这个属性了解用户的环境信息。
    • 属性
      • Navigator.userAgent 浏览器请求头
      • Navigator.plugins 返回一个类似数组的对象,成员是 Plugin 实例对象,表示浏览器安装的插件
      • Navigator.platform 返回用户的操作系统信息
      • Navigator.onLine 表示用户当前在线还是离线
      • Navigator.language,Navigator.languages 表示浏览器的首选语言
      • Navigator.geolocation 返回一个 Geolocation 对象,包含用户地理位置的信息
      • Navigator.cookieEnabled 表示浏览器的 Cookie 功能是否打开。
      • Navigator 对象的方法
      • Navigator.javaEnabled() 表示浏览器是否能运行 Java Applet 小程序。
      • Navigator.sendBeacon() 用于向服务器异步发送数据
      • Navigator 的实验性属性(在部分浏览器可用)
      • Navigator.deviceMemory 返回当前计算机 的内存数量(单位为 GB)
      • Navigator.hardwareConcurrency 返回用户计算机上可用的逻辑处理器的数量
      • Navigator.connection 包含当前网络连接的相关信息
  • Screen 对象

    • Screen.height:浏览器窗口所在的屏幕的高度(单位像素)。
    • Screen.width:浏览器窗口所在的屏幕的宽度(单位像素)。
    • Screen.availHeight:浏览器窗口可用的屏幕高度(单位像素)。因为部分空间可能不可用,比如系统的任务栏或者 Mac 系统屏幕底部的 Dock 区,这个属性等于height减去那些被系统组件的高度。
    • Screen.availWidth:浏览器窗口可用的屏幕宽度(单位像素)。
    • Screen.pixelDepth:整数,表示屏幕的色彩位数,比如24表示屏幕提供24位色彩。
    • Screen.colorDepth:Screen.pixelDepth的别名。严格地说,colorDepth 表示应用程序的颜色深度,pixelDepth 表示屏幕的颜色深度,绝大多数情况下,它们都是同一件事。
    • Screen.orientation:返回一个对象,表示屏幕的方向
  • Cookie

    • Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
    • Cookie 主要保存状态信息,以下是一些主要用途。
      • 对话(session)管理:保存登录、购物车等需要记录的信息。
      • 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等。
      • 追踪用户:记录和分析用户行为。
      • Cookie 不是一种理想的客户端储存机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端储存应该使用 Web storage API 和 IndexedDB。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面。
    • Cookie的生成
      • 服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段。
      • document.cookie 用于读写当前网页的 Cookie,读取的时候,它会返回当前网页的所有 Cookie,前提是该 Cookie 不能有HTTPOnly属性。
      • document.cookie属性是可写的,可以通过它为当前网站添加 Cookie。
      • document.cookie写入 Cookie 的例子如下。
        • document.cookie ="dta=dta; expires=Thu, 17 Jun 2022 15:34:17 GMT; path=/; domain=www.dtasecurity.cn"
      • 删除一个现存 Cookie 的唯一方法,是设置它的expires属性为一个过去的日期。
  • Storage 接口

    • 两个对象部署了这个接口:window.sessionStorage和window.localStorage
      • sessionStorage保存的数据用于浏览器的一次会话(session),当会话结束(通常是窗口关闭),数据被清空;
      • localStorage保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。
      • 除了保存期限的长短不同,这两个对象的其他方面都一致。
    • 属性和方法
      • Storage 接口只有一个属性。
        • Storage.length:返回保存的数据项个数。
      • Storage.setItem()
        • 用于存入数据。它接受两个参数,第一个是键名,第二个是保存的数据
        • 写入不一定要用这个方法,直接赋值也是可以的。
      • Storage.getItem()
        • 用于读取数据。它只有一个参数,就是键名
      • Storage.removeItem()
        • 用于清除某个键名对应的键值。
      • Storage.clear()
        • 用于清除所有保存的数据
      • Storage.key()
        • 接受一个整数作为参数(从零开始),返回该位置对应的键名
        • 结合使用Storage.length属性和Storage.key()方法,可以遍历所有的键。
  • History 对象

    • window.history属性指向 History 对象,它表示当前窗口的浏览历史。
    • 属性:
      • History.length:当前窗口访问过的网址数量(包括当前网页)
      • History.state:History 堆栈最上层的状态值 (通常是 undefined,即未设置)
    • 方法
      • History.back() 移动到上一个网址,等同于点击浏览器的后退键
      • History.forward() 移动到下一个网址,等同于点击浏览器的前进键
      • History.go() 接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址
      • History.pushState(state, title, url) 在历史中添加一条记录
      • History.replaceState() 修改 History 对象的当前记录
  • Location 对象

    • Location对象是浏览器提供的原生对象,提供 URL 相关的信息和操作方法。通过window.location和document.location属性,可以拿到这个对象。
    • 属性:
      • Location.href:整个 URL。
      • Location.protocol:当前 URL 的协议,包括冒号(:)。
      • Location.host:主机。如果端口不是协议默认的80和433,则还会包括冒号(:)和端口。
      • Location.hostname:主机名,不包括端口。
      • Location.port:端口号。
      • Location.pathname:URL 的路径部分,从根路径/开始。
      • Location.search:查询字符串部分,从问号?开始。
      • Location.hash:片段字符串部分,从#开始。
      • Location.username:域名前面的用户名。
      • Location.password:域名前面的密码。
      • Location.origin:URL 的协议、主机名和端口。
    • 方法:
      • assign
        • assign方法接受一个 URL 字符串作为参数,使得浏览器立刻跳转到新的 URL。Location.replace()
      • replace
        • 同上,但是会在浏览器的浏览历史History里面删除当前网址,后退按钮就无法回到当前网页了,它的一个应用是,当脚本发现当前是移动设备时,就立刻跳转到移动版网页。
      • reload
        • 使得浏览器重新加载当前网址,相当于按下浏览器的刷新按钮。
      • toString
        • toString方法返回整个 URL 字符串,相当于读取Location.href属性。
  • URL 的编码和解码

    • URL 元字符:分号(;),逗号(,),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)
    • 语义字符:a-z,A-Z,0-9,连词号(-),下划线(_),点(.),感叹号(!),波浪线(~),星号(*),单引号('),圆括号(())
    • encodeURI()
      • 用于转码整个 URL。它的参数是一个字符串,代表整个 URL。它会将元字符和语义字符之外的字符,都进行转义。
    • encodeURIComponent()
      • 用于转码 URL 的组成部分,会转码除了语义字符之外的所有字符.
    • decodeURI()
      • encodeURI()方法的逆运算
    • decodeURIComponent()
      • encodeURIComponent()方法的逆运算

document

  • 常用方法
    • document.open方法清除当前文档所有内容,使得文档处于可写状态,供document.write方法写入内容。
    • document.close方法用来关闭
    • document.open()打开的文档。
    • document.querySelector(),document.querySelectorAll()接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null
    • document.getElementsByTagName() 方法搜索 HTML 标签名,返回符合条件的元素。
    • document.getElementsByClassName() 返回一个类似数组的对象,包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中。
    • document.getElementsByName() 选择拥有name属性的 HTML 元素
    • document.getElementById() 返回匹配指定id属性的元素节点
    • document.createElement() 生成元素节点,并返回该节点。
    • document.createTextNode()用来生成文本节点(Text实例),并返回该节点。
    • document.createAttribute()生成一个新的属性节点(Attr实例),并返回它。
    • document.createEvent方法生成一个事件对象(Event实例),该对象可以被element.dispatchEvent方法使用,触发指定事件。
    • document.addEventListener(),document.removeEventListener()添加事件监听函数、移除事件监听函数

canvas

  • 基本元素(参考极验滑块绘制图片)
        c = createElement('canvas')
        c['width'] = 123
        c['height'] = 123
        ctx = c['getContext']('2d')
        ctx['drawImg'](img, x, y)
    
        c2 = createElement('canvas')
        ctx2 = c2['getContext']('2d')
        img_data = c['getImageData'](x, y, width, height)
        ctx2['putImageData'](img_data, x, y)
    
posted @ 2022-03-25 13:51  liDB  阅读(618)  评论(0编辑  收藏  举报