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。
- 如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。
-
undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值。
-
null:表示空值,即此处的值为空。
-
对象(object):各种值组成的集合。
对象
- 查看一个对象本身的所有属性,可以使用Object.keys方法。
- 删除属性
- delete命令用于删除对象的属性,删除成功后返回true
- 删除一个不存在的属性,delete不报错,而且返回true
- 只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。(定义属性不能删除,如defineProperty)
- delete命令只能删除对象本身的属性,无法删除继承的属性
- 判断属性是否存在
- in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。
- hasOwnProperty:方法判断一下,是否为对象自身的属性。
- 遍历属性
- for...in循环用来遍历一个对象的全部属性。
- 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
- 它不仅遍历对象自身的属性,还遍历继承的属性
- 只想遍历对象自身的属性,应该结合使用hasOwnProperty方法
- for...in循环用来遍历一个对象的全部属性。
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 } });
- 可以重写get set,来进行hook
- 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():判断某个属性是否可枚举。
- Object.prototype.valueOf() 返回当前对象对应的值。
-
创建对象的方式
- 直接赋值,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
- prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
-
包装对象
- 对象是 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 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
- 根据 JavaScript 的语法,圆括号()跟在函数名之后,表示调用该函数。
-
属性和方法
- name 属性 返回函数的名字
- length 属性 返回函数预期传入的参数个数
- toString() 返回一个字符串,内容是函数的源码
- 通过断点调试可以查看到
-
作用域
- 作用域(scope)指的是变量存在的范围。
- 在 ES5 的规范中,JavaScript 只有两种作用域:
- 一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;
- 另一种是函数作用域,变量只在函数内部存在。
- ES6 又新增了块级作用域。
- 函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
- 在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)。
- 局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
- 函数本身的作用域就是其声明时所在的作用域,与其运行时所在的作用域无关。
- 函数体内部声明的函数,作用域绑定函数体内部(闭包)
- 作用域(scope)指的是变量存在的范围。
-
变量名提升
- 与全局作用域一样,函数作用域内部也会产生“变量提升”现象。
- var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
-
参数的省略
- 函数参数不是必需的,JavaScript 允许省略参数。
- 函数的length属性与实际传入的参数个数无关,只反映函数预期传入的参数个数。
-
传参
- 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。
- 这意味着,在函数体内修改参数值,不会影响到函数外部。
- 如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。
- 也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。
- 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。
-
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 - 由于数组本质上是一种对象,所以可以为数组添加属性
- length 属性
- 遍历
- 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方法,用来为该节点定义事件的监听函数。
- HTML 的 on- 属性
- 事件的传播
- 一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
- 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
- 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
- 这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
- 一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
- 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()方法,可以遍历所有的键。
- Storage 接口只有一个属性。
- 两个对象部署了这个接口:window.sessionStorage和window.localStorage
-
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属性。
- assign
-
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)