JavaScript
DOM
DOM的操作
创建节点
- createElement
创建元素节点,接收一个参数,参数即元素标签名
const divEl = document.createElement(“div”); - createTextNode
创建一个文本节点
const textEl = document.createTextNode(“content”); - createDocumentFragment
创建一个文档碎片,表示一种轻量级的文档,用来存储临时节点,然后将文档碎片的内容一次性添加到DOM中
const fragment = document.createDocumentFragment();
当把DocumentFragment节点插入文档时,插入的不是DocumentFragment本身,而是它的子孙节点。 - createAttribute
创建属性节点,可以是自定义属性
const dataAttribute = document.createAttribute(‘custom’);
console.log(dataAttribute);
获取节点
- querySelector
传入任何有效的css选择器,即可选中单个DOM元素(首个)
document.querySelector(‘.element’) #选择带有类名的元素
document.querySelector(‘#element’) #选择带有ID的元素
document.querySelector(‘div’)
document.querySelector(‘[name=”username”]’) #选择属性为指定值的元素
document.querySelector(‘div + p > span’) #p是div的相邻兄弟元素,span是p的直接子元素
注:如果页面没有选择的元素,返回null - querySelectorAll
返回一个包含节点子树内所有与之相匹配的Element节点列表,如果没有相匹配的,则返回一个空节点列表
const notLive = document.querySelectorAll(“p”);
注意:该方法返回的是NodeList的静态实例,它是一个静态的”快照”,而非”实时”的查询
更新节点
- innerHTML
不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树
// 获取<p id=”class”>…</p>
var p = document.getElementById(‘class’);
// 设置文本为ABC
p.innerHtML = ‘ABC’; // <p id=”class”>ABC</p> - innerText、textContent
使用这两个元素设置文本时,如若写的是HTML标签,网页会将它处理为普通文本。防止用户输入的内容包含恶意的HTML代码。从而增强网页的安全性!
// 设置文本
p.innerText = ‘<script>alert(“Hi”)</script>’;
// 上述代码显示内容为
// <script>alert(“Hi”)</script>
两者的区别:innetText不返回隐藏元素的文本,textContent返回所有文本 - style
DOM节点的style属性对应所有的CSS,可以直接获取或设置,遇到 - 需要转换为驼峰命名
// 获取<p id=”p-id”>…</p>
const p = document.getElementById(‘p-id’);
// 设置CSS
p.style.color = ‘#ff0000’;
p.style.fontSize = ‘20px’;
p.style.paddingTop = ‘2em’;
添加节点
- innerHTMl
如果DOM节点是空的,例如<div></div>,那么,直接使用innerHTML =’<span>child</span>’,那么就可以修改DOM节点的内容,相当于添加了新的DOM节点。如果这个DOM节点不是空的,就不能这么做!因为innerHTML会直接替换掉原来的所有子节点。 - appendChild
把一个子节点添加到父节点的最后一个子节点
举个例子:
添加一个p元素
现在HTML结构变成了下面
我们先是获取DOM元素再进行添加操作,这个js节点是已经存在当前文档树中,因此这个节点首先会从原先的位置删除.,再插入到新的位置。
也可以创建一个新的节点,然后插入到指定的位置。 - insertBefore
把子节点插入到指定的位置,使用方法如下:
parentElement.insertBefore(newElement,referenceElement)
子节点会插入到referenceElement之前 - setAttribute
在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值
const div = document.getElementById(‘id’)
div.setAttribute(‘class’,’white’); // 参数名 参数值
删除节点
删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉
删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时添加到别的位置
BOM的介绍及常见的BOM对象
BOM是什么?
BOM,浏览器对象模型,提供了独立内容,与浏览器窗口进行交互的对象。作用就是和浏览器做一些交互效果,比如页面的后退,刷新,浏览器窗口变化。以及获取用户的一些信息:浏览器品牌版本,屏幕分辨率
浏览器的内容可以看成DOM,整个浏览器可以看成BOM。区别如下:
window
BOM的核心对象是window,它表示浏览器的一个实例
浏览器中,window对象有双重角色,既是浏览器窗口的一个接口,又是全局对象。因此在全局作用域中声明的变量、函数都会变成window对象的属性和方法。
窗口控制方法:moveBy(x,y)、moveTo(x,y)….(度娘了解)
window.open() 既可以导航到一个特定的url,也可以打开一个新的浏览器窗口(取决于参数)
如果window.open()传递了第二个参数,且该参数是已有窗口或者框架的名称,那么就会在目标窗口加载第一个参数指定的url
window.open(‘http://www.vue3js.cn','topFrame‘)
window.open()会返回新窗口的引用,也就是新窗口的window对象
const myWin = window.open(‘http://www.vue3js.cn','myWin‘)
window.close()仅作用于window.open()打开的窗口
新建的window对象有一个opener属性,该属性指向性打开他的原始窗口对象
location
url地址:
http://foouser:barpassword@www.wroc.com:80/WileyCDA/?q=javascript#contents
location 属性如下:
除了hash之外,只要修改location的一个属性,就会导致页面重新加载新的URL。location.reload(),此方法会根据最有效的方式刷新页面,如果页面内容没有发生改变,就会从浏览器缓存中重新加载。如果强制从服务器重新加载,传递一个参数true即可。
navigator
navigator 对象主要是用来获取浏览器的属性,区分浏览器的类型。
screen
screen对象可以提供关于用户屏幕的信息,如屏幕尺寸,颜色深度等。方便开发者更好地了解用户设备的屏幕环境,从而进行相应的页面布局和样式调整。
history
history对象主要操作浏览器的历史纪录,可以通过参数向前,向后,或者向指定URL跳转
history.go()
接受参数为字符串时:向最近的一个记录中包含指定字符串的页面跳转
history.go(‘maixiaofei.con’)
接受参数为整数时:正数表示向前跳转指定页数的页面,负数为向后跳转指定页数的页面
history.go(3) // 向前跳转三个记录
history.go(-1) // 向后跳转一个记录
history.forward(): 向前跳转一个页面
history.back(): 向后跳转一个页面
history.length: 获取历史记录数
== 和 ===区别,分别在什么情况使用
等于操作符
等于操作符用两个等号(==)表示,如果操作数相等,则返回true
前面已经提到,在JavaScript中存在隐式转换。等于操作符(==)在比较中会先进行类型转换,再确定操作数是否相等。转换规则如下:
如果任一操作数是布尔值,则将其转换为数值再比较是否相等
1 | let result1 = (true == 1) // result1 赋值为true |
如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等
1 | let result1 = ("55" == 55) // true |
如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法取得其原始值,再进行比较
1 | let obj = {valueOf:function(){return 1}} // 覆盖valueOf()方法 |
null 和 undefined相等
1 | let result1 = (null == undefined); // true |
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则操作符返回true
1 | let obj1 = {name:"xxx"} |
一个小总结:
全等操作符
只有两个操作数在不转换的前提下相等才返回true。即类型相同,值也相同
1 | let result1 = ("55" === 55); // flase |
let result1 = (null === null); // true
let result1 = (undefined === undefined); // true
区别
- 相等操作符(==) 会做类型转换,再进行值的比较,全等运算符不会做类型转换
- null 和 undefined 比较,相等操作符(==)为true,全等操作符为false
小结
相等运算符隐藏的类型转换,可能会带来一些违反直觉的结果
1 | '' == '0' // false 相等类型比较 |
一般比较null的时候,我们一般使用相等操作符,因为这样的写法更加明显简洁。
1 | const obj = {}; |
所以,除了在比较对象属性为null或者undefined的情况下,我们使用相等操作符(==),其它情况一律使用全等操作符!
typeof 与 instanceof 区别
typeof
typeof 操作符返回一个字符串,表示未经计算的操作数的类型。
对于计算过的操作数,无法使用typeof获取其类型,因为计算过的操作数,其结果会被存储在一个变量中,而不再保留其原始类型信息。
对于 a = b + c ; b 和 c 就是计算过的操作数。
使用方法:
1 | typeof operand |
举个例子
1 | typeof 1 // 'number' |
虽然typeof null 为object, 但这只是JavaScript存在的BUG,不代表null是引用数据类型。所以,如果需要在if语句中判断是否为
null类型,直接通过===null判断就好。
同时,可以发现,对于引用数据类型,除了function会被识别出来,其余的都输出object。
如果我们想要判断一个变量是否存在,可以使用typeof : (不能使用if(a),若a未声明,则报错)
1 | if(typeof a != 'undefined') |
instanceof
instanceof 运算符用于检测构造函数的prorotype属性是否出现在某个实例对象的原型链上
使用如下:
1 | object instanceof constructor |
其中,object 为实例对象, constructor为构造函数
1 | //定义构造函数 |
instanceof的原理如下:
1 | function myInstanceof(left,right) |
区别
typeof 与 instanceof 都是判断数据类型的方法,主要区别如下:
- typeof 会返回一个变量的基本类型,instanceof 返回的是一个布尔值
- instanceof 可以准确地判断复杂引用数据类型,但不能准确判断基本数据类型
- typeof 虽然可以判断基本数据类型(null除外),但是引用数据类型中,除了function类型外,其它的无法判断
所以,上述两种方法都有弊端,并不满足所有的场景需求
如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式”[object xxx]”的字符串
了解toString的基本用法,就可以实现一个全局通用的数据类型判断方法
JavaScript原型?原型链?有什么特点?
原型
JavaScipt 常被描述为一种基于原型的语言 —- 每个对象拥有一个原型对象。当试图访问一个对象的熟悉时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及对象的原型的原型,依次向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
准确地说,这些属性和方法定义在Object的构造函数的prototype属性上,而非实例对象本身。
下面举个例子:
函数可以有属性。每个函数都有一个特殊的属性叫做原型prototype
1 | function dosomething(){} |
控制台输出:
上面这个对象,就是常说的原型对象
可以看到,原型对象有一个自带属性constructor,这个属性指向该函数。
原型链
原型对象也可能拥有原型,并从中继承方法和属性。这种关系常被称为原型链,它解释了为何对象会拥有定义在其它对象中的属性和方法。
在对象实例和它的构造器之间建立一个链接(它是_proto_属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
1 | function Person(name) |
根据代码有下图关系
分析一下:
- 构造函数Person存在原型对象Person.prototype
- 构造函数生成实例对象person,person的_proto_指向构造函数Person的原型对象
- Person.prototype.proto 指向内置对象,因为Person.prototype 是个对象,默认是Object函数创建的,而Object.prototype为内置对象
- Person._propo_指向内置匿名函数anonymous,因为Person是个函数对象,默认由Function创建
- Function.prototype 和 Function.proto 同时指向内置匿名函数anonymous,这样原型链的终点就是null
总结
_proto_作为不同对象之间的桥梁,用来指向创建它的构造函数的原型对象
1 | person1._proto_ === Person.prototype |
构造函数是一个函数对象,是通过Function函数产生的
1 | Person._proto_ === Function.prototype |
原型对象本身是一个普通对象,而普通对象的构造函数都是Object
1 | Person.prototype._proto_ === Object.prototype |
所有的构造器都是函数对象,函数对象都是Function构造产生的
1 | Object._proto_ === Function.prototype |
Object的原型对象也有_proto_属性指向null,null是原型链的顶端
1 | Object.prototype._propo_ === null |
总结:
- 一切对象都是继承Object对象,Object对象直接继承根源对象null
- 一切的函数对象(包括Object对象),都是继承自Function对象
- Object对象直接继承来自Function对象
- Function对象的_proto_会指向自己的原型对象,最终还是继承自Object对象