js css html

50个JS的高级知识点

2022-01-10  本文已影响0人  没名字的某某人

1. undeclared 与 undefined 的区别?

var a // undefined
b; // b is not defined

2. let & const 与 var 的区别?

3. 暂时性死区问题

var a = 100;
if(1){
  a = 10;
  // 在当前块作用域中存在a使用let/const 声明的情况,给a赋值10时,只会在当前作用域找变量a
  // 而这时,还未到声明时候,所以控制台 抛出引用错误:Cannot access 'a' before initialization
   let a = 1;
}

4. 获取DOM元素有哪些方法

注意:document 可以换成 dom节点,那么搜索范围在dom节点的子节点内

方法 描述 返回
document.getElementById(id) 通过id获取dom 符合条件的dom对象
document.getElementsByTagName(tagName) 通过标签名获取dom 符合条件的所有dom对象组成的类数组
document.getElementsByClassName(class) 通过class获取dom 符合条件的所有dom对象组成的类数组
document.getElementsByName(name) 通过标签的属性name获取dom 符合条件的所有dom对象组成的类数组
document.querySelector(选择器) 通过选择器获取dom 符合条件的第一个dom对象
document.querySelectorAll(选择器) 通过选择器获取dom 符合条件的所有dom对象组成的类数组

5. 操作DOM元素有哪些方法

标题 描述
createElement 创建一个标签节点
createTextNode 创建一个文本节点
cloneNode(deep) 复制一个节点,连同属性与值都复制,deep为true时,连同后代节点一起复制,否则只复制当前节点
createDocumentFragment 创建一个文档碎片节点
appendChild 追加子元素
insertBefore 将元素插入前面
removeChild 删除子元素
replaceChild 替换子元素
getAttribute 获取节点的属性
createAttribute 创建属性
setAttribute 设置节点属性
removeAttribute 删除节点属性
element.attributes 将属性生成类数组对象

6. DOM的类型有哪几种?

7. js的作用域及作用域链

8. 数组的splice 与 slice 有什么区别

方法 参数 描述
splice splice(start,num,item1,item2..) 从start索引开始,截取num个元素,并插入item1、item2到原数组里,影响原数组
slice slice(start,end) 从start开始,截取到end-1,如果没有end,则截取到最后一个元素,不影响原数组

9. substr 和 substring 的区别?

方法 参数 描述
substr substr(start, length) 返回从start位置开始length长度的字符串
substring substring(start, end) 返回从start位置到end-1位置的字符串

10. includes 比 indexOf 好在哪

includes 可以检测 NaNindexOf不能检测NaN

11. for循环中使用定时器

for(var i = 0; i<3;i++){
  setTimeout(function(){
    console.log(i);
  },0);  // 3,3,3
}

解决方法一:

for(let i = 0; i<3;i++){
  setTimeout(function(){
    console.log(i);
  },0);  // 1,2,3 
}

解决方法二:

for(let i = 0; i<3;i++){
  (function(i){
    setTimeout(function(){
        console.log(i);
      },0);  // 1,2,3 
  })()
}

12. Promise解决了什么问题

13. async/await 解决了什么问题

对于 async/await ,我总结为一句话async/await 是generator + Promise 的语法糖,它用同步的方式执行异步代码

15. JS延迟加载的方法有哪些?

16. new 操作符的原理

分析下new的整个过程

简单实现下new:

function myNew(fn, ..args){
  // 第一步
  const obj = {};
  // 第二步
  obj._proto_ = fn.prototype
  // 第三步
  fn.apply(obj,args)
  // 第四步
  return obj
}

17. 什么是文档碎片

var oFragmeng = document.createDocumentFragment(); 


for(var i=0;i<10000;i++)

{ 

    var op = document.createElement("span"); 

    var oText = document.createTextNode(i); 

    op.appendChild(oText); 

    //先附加在文档碎片中

    oFragmeng.appendChild(op);  

} 


//最后一次性添加到document中

document.body.appendChild(oFragmeng); 

19. 宏任务与微任务有哪些

宏任务

* 浏览器 node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

微任务

* 浏览器 node
I/O
Promise.prototype.then catch finally
process.nextTick
MutationObserver

21. Object.defineProperty 方法

22. 防抖和节流有哪些应用场景

操作 描述 场景
防抖 频繁去触发一个事件,但是只触发最后一次。以最后一次为准 1、电脑息屏时间,每动一次电脑又重新计算时间2、input框变化频繁触发事件可加防抖3、频繁点击按钮提交表单可加防抖
节流 频繁去出发一个时间,但是只能每隔一段时间触发一次 1、滚动频繁请求列表可加节流2、游戏里长按鼠标,但是动作都是每隔一段时间做一次

23. 什么是高阶函数

js的函数其实都是指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称为高阶函数。

24. 什么是函数柯里化

是把接收多个参数的函数变换成接收一个单一参数的函数,并且返回接收余下的参数而且返回结果的新函数技术。

// 普通的add函数
function add(x, y){
  return x+y
 }

// currying 后
funtion curryingAdd(x){
  return funtion(y){
    return x+y
  }
}
add(1,2) //3
curryingAdd(1)(2)

好处

  1. 参数复用
  2. 延迟执行
    其实Function.prototype.bind 就是柯里化的实现例子
function sayKey(key){
  console.log(this[key])
}
const person={
  name:'Sunshine',
  age:23
}
// call 不是柯里化
sayKey.call(person,'name')  // 立即输出 Sunshine

// bind是柯里化
const say = sayKey.bind(person)  // 不执行
// 想执行再执行
say('name')  // 输出 Sunshine

25. 什么是 compose

简单的compose函数

const compose = (a,b)=>c=>a(b(c));

例子: 统计单词个数

const space = (str) => str.split(' ' );
const len = (arr) => arr.length;

// 普通写法
console.log(len(space('i am linsanxin')))  // 3

// compose 写法
const compose = (...fn)=> value => {
  return fn.reduce((value, fn)=>{
    return fn(value)
  }, value)
}
const computed = compose(space, len)
console.log(computed('i am linsanxin'))  // 3

26. 箭头函数与普通函数的区别

27. Symbol的应用场景

场景1 使用Symbol来作为对象属性名
平常我们的对象属性都是字符串,其实也可用Symbol来当做属性名

const gender = Symbol('gender');
const obj = {
  name: 'sunshine',
  age: 23,
  [gender]: '男'
 }
console.log(obj[gender])  // 男

但是Symbol 作为属性的属性不会被枚举出来,这就是JSON.stringfy(obj)时,Symbol属性会被排除在外的原因
其实想获取 Symbol 属性也不是没有办法

// 方法一
console.log(Object.getOwnPeopertySymbols(obj))  // [Symbol(gender)]
// 方法二
console.log(Reflect.ownKeys(obj))  // ['name', 'age', Symbol(gender)]

场景2 使用Symbol 来代替常量

// 赋值
const one = 'oneXin'
const two = 'twoXin'
 function fun(key){
  switch (key){
    case one:
      return 'one'
      break;
    case tow:
      return 'two'
      break;
  }
}

如果变量少的话还好,如果变量多的时候,赋值变量名很烦,可以利用Symbol的唯一性

const one = Symbol()
const two = Symbol()

应用场景3 使用Symbol 定义类的私有属性

28. AMD 和 CMD 的区别

模块化 代表应用 特点
AMD require.js 1. AMD的api默认一个当多个用 2. 依赖前置,异步执行
CMD sea.js 1. CMD的api严格区分,推崇职责单一 2. 依赖就近,按需加载,同步执行

29. Commonjs 和 ES6 Module 的区别

30. 为什么Commonjs 不适用于浏览器

var math = require('math')
math.add(2, 3)

第二行math.add(2, 3) ,在第一行之后运行,因此必须等math.js加载完成。而前端加载时间取决于网速的快慢,等待时间浏览器会处于假死状态,因此,浏览器的模块不能采用‘同步加载’,只能采用‘异步加载’,这就是AMD规范诞生的背景。

36. 深度遍历与广度遍历的区别

对于算法来说无非就是时间换空间,空间换时间

37. js中的设计模式

38. forEach如何跳出循环

forEach是不用通过break 或者 return 来实现跳出循环的,forEach的回调函数形成了一个作用域,在里面使用return 并不会跳出,只会被当做continue,可以利用try catch

function getItemById(arr, id){
  var item = null;
  try{
    arr.forEach(function (curItem, i){
      if(curItem.id == id){
        item = curItem;
        throw Error();
      }
    })
  }catch(e){}
  return item;
}

39、JS中如何将页面重定向到另一个页面?

41. 鼠标事件有哪些

注明:鼠标左中右键看event 对象上的button 属性,对应1,2,3

事件 说明
click 点击鼠标左键,在用户焦点在按钮并按下enter,也会触发
dbclick 双击鼠标左键触发
mousedown 单击鼠标任意一个按键都触发
mouseout 鼠标指针位于某个元素上且将要移出元素边界时触发
mouseover 鼠标指针移出某个元素到另一个元素上时触发
mouseup 松开任意鼠标按键时触发
mousemove 鼠标在某个元素上时持续发生
mouseenter 鼠标进入某个元素边界时触发
mouseleave 鼠标离开某个元素边界时触发

42. 键盘事件

事件 说明
onkeydown 某个键盘按键被按下时触发
onkeyup 某个键盘按键被松开时触发
onkeypress 某个按键被按下时触发,不监听功能键,如ctrl,shift

43、JS中鼠标事件的各个坐标

属性 说明 兼容性
offsetX 以当前的目标元素左上角为原点,定位x轴坐标 除Mozilla外都兼容
offsetY 以当前的目标元素左上角为原点,定位y轴坐标 除Mozilla外都兼容
clientX 以浏览器可视窗口左上角为原点,定位x轴坐标 都兼容
clientY 以浏览器可视窗口左上角为原点,定位y轴坐标 都兼容
pageX 以doument对象左上角为原点,定位x轴坐标 除IE外都兼容
pageY 以doument对象左上角为原点,定位y轴坐标 除IE外都兼容
screenX 以计算机屏幕左上顶角为原点,定位x轴坐标(多屏幕会影响) 全兼容
screenY 以计算机屏幕左上顶角为原点,定位y轴坐标 全兼容
layerX 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 x 轴坐标 Mozilla 和 Safari
layerY 最近的绝对定位的父元素(如果没有,则为 document 对象)左上顶角为元素,定位 y 轴坐标 Mozilla 和 Safari

44. js中元素视图的各个尺寸

属性 说明
offsetLeft 获取当前元素到定位父节点的left方向的距离
offsetTop 获取当前元素到定位父节点的top方向的距离
offsetWidth 获取当前元素 width + 左右padding + 左右border-width
offsetHeight 获取当前元素 height + 上下padding + 上下border-width
clientWidth 获取当前元素 width + 左右padding
clientHeight 获取当前元素 height + 上下padding
scrollWidth 当前元素内容真实的宽度,内容不超出盒子宽度时为盒子的clientWidth
scrollHeight 当前元素内容真实的高度,内容不超出盒子高度时为盒子的clientHeight

45. window视图的各个尺寸

属性 说明
innerWidth innerWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
innerHeight innerHeight 浏览器可视窗口可视区高度(不包括浏览器控制台、菜单栏、工具栏)

46. Document 文档视图的各个尺寸

属性 说明
document.documentElement.clientWidth 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
document.documentElement.clientHeight 浏览器窗口可视区宽度(不包括浏览器控制台、菜单栏、工具栏)
document.documentElement.offsetHeight 获取整个文档的高度(包含body的margin)
document.body.offsetHeight 获取整个文档的高度(不包含body的margin)
document.documentElement.scrollTop 获取文档的滚动top方向的距离(当窗口发生滚动时发生滚动时值改变)
document.documentElement.scrollLeft 获取文档的滚动left方向的距离(当窗口发生滚动时值改变)

9个高级JavaScript方法

1. getBoundingClientRect

Element.getBoundingClientRect()方法返回元素的大小及其相对视口的位置。返回的是一个对象,对象李包含了8个属性:left, right, top, bottom, width, height, x, y

watermark1.jpg

可以根据getBoundingClientRect 方法判断当前元素是否完整出现在视口里

// html

<div id="box"></div>

body {
       height: 3000px;
       width: 3000px;
      }

#box {
       width: 300px;
       height: 300px;
       background-color: red;
       margin-top: 300px;
       margin-left: 300px;
    }
    
// js

const box = document.getElementById('box')
        window.onscroll = function () {
            // box完整出现在视口里才会输出true,否则为false
            console.log(checkInView(box))
        }

function checkInView(dom) {
        const { top, left, bottom, right } = dom.getBoundingClientRect()
        console.log(top, left, bottom, right)
        console.log(window.innerHeight, window.innerWidth)
        return top >= 0 &&
                left >= 0 &&
                bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
                right <= (window.innerWidth || document.documentElement.clientWidth)
        }

根据这个用处,咱们可以实现:懒加载和无限滚动
注意,该方法每次scroll都得重新计算,性能耗费打,引起重绘回流。

2. IntersectionObserver

2.1 是什么
IntersectionObserver 接口提供了一种异步观察目标元素与祖先元素或顶级文档视窗交叉状态的方法。祖先元素与视窗被称为根(root)
通俗点说:IntersectionObserver 是用来监听某个元素与视口的交叉状态的。如下图:一开始整个元素都在视口内,那么元素与视口交叉状态就是100%,而后滚动,元素只显示一半,那么交叉状态就是50%。

watermark2.jpg

2.2 用法

// 接收两个参数 callback option
let io = new IntersectionObserver(callback, option);

// 开始观察(可观察多个元素)
io.observe(document.getElementById('example1'));

// 停止观察某个元素
io.unobserve(element);

// 关闭观察
io.disconnect();

2.3 callback
callback 一般有两种触发情况,一种是目标刚刚进入视口,另外一种是完全离开视口。

let io = new IntersectionObsever(
  entries=>{
     console.log(entries);
  }
);

callback 函数的参数是(entries)一个数组,每个成员都是一个 IntersectionObserverEnter 对象。

2.4 IntersectionObserverEnter 对象

{
  time: 3893.92,
  rootBounds: ClientRect {
    bottom: 920,
    height: 1024,
    left: 0,
    right: 1024,
    top: 0,
    width: 920
  },
  boundingClientRect: ClientRect {
     // ...
  },
  intersectionRect: ClientRect {
    // ...
  },
  intersectionRatio: 0.54,
  target: element
}

属性解析:

2.5 option
第二个参数里有两个重要的属性:threshold和root
threshold 属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例达到0时触发回调函数。

new IntersectionObserver(
  entries=>{ /* ... */ },
  { threshold:[0,0.25,0.5,0.75,1] }
)

用户可以自定义这个数组。比如[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。
root 属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

new IntersectionObserver({entries=>{/*...*/}},{
  threshold: [0],
  root: document.getElementById('#container')
})

2.6 完整例子

body {
            height: 3000px;
            width: 3000px;
        }

#box1 {
            width: 300px;
            height: 300px;
            background-color: red;
            margin-top: 100px;
            margin-left: 300px;
        }
#box2 {
            width: 300px;
            height: 300px;
            background-color: red;
            margin-top: 100px;
            margin-left: 300px;
        }
<div id="box1"></div>
<div id="box2"></div>

const io = new IntersectionObserver(entries => {
            console.log(entries)
        }, {
            threshold: [0, 0.25, 0.5, 0.75, 1]
            // root: xxxxxxxxx
        })
io.observe(document.getElementById('box1'))
io.observe(document.getElementById('box2'))

2.6 使用场景
可以像getBoundingClientRect 那样判断元素是否在视口,做到懒加载和无限滚动功能,且不会引起重绘回流

3. createNodeIterator

节点原生遍历,详细参数
)

const body = document.getElementsByTagName('body')[0]
const it = document.createNodeIterator(body)
let root = it.nextNode()
while(root){
  console.log(root)
  root = it.nextNode()
}
4. getComputedStyle

window.getComputedStyle() 方法返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有css属性值。私有的css属性值可以通过对象提供的API或通过简单地使用CSS属性名称进行索引来访问。

window.getCoumtedStyle(element, perudoElement)

配合 getPropertyValue 可以获取到具体样式

// html
#box {
  width: 640px;
  height: 200px;
  background-color: yellow;
}
<div id="box"></div>
//js
const box = document.getElementById('box')
const styles = window.getComputedStyle(box)
const height = styles.getPropertyValue('height')
const width = styles.getPropertyValue('width')
console.log(height, width)
5. requestAnimationFrame

JS动画,参考requestAnimationFrame理解与实践

6. requestIdleCallback

requestIdleCallback回调的执行的前提条件是当前浏览器处于空闲状态。

7. DOMContentLoaded

7.1 是什么
当初始的HTML文档被完全加载和解析之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。
浏览器向服务器请求到了HTML 文档后便开始解析,产物是DOM,到这里HTML文档被加载和解析完成了入股有CSS的会根据CSS生成CSSOM,然后再由DOM和CSSOM合并产生渲染树。有了渲染树,知道了所有节点的样式,下面便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局阶段,节点绘制到浏览器的过程如下图所示:

watermark3.jpg

js可以阻塞DOM生成,也就是说当浏览器解析HTML文档时,如果遇到

<body>
  <script type="text/javascript">
  console.log(document.getElementById('ele')); // null
  </script>

  <div id="ele"></div>

  <script type="text/javascript">
  console.log(document.getElementById('ele')); // <div id="ele"></div>
  </script>
</body>

另外,因为js可以查询任何对象的样式,所以意味着在CSS解析完成,也就是CSSOM生成之后,js才能被执行。

7.2 异步脚本
如果我们想要页面尽快显示,那我们可以使用异步脚本。H5中定义了两个异步脚本的方法:defer 和async。
同步脚本(标签不包含async和defer),当HTML文档解析时遇见的(同步)脚本,则停止解析,先去加载脚本,然后执行,执行结束后继续解析HTML文档。过程如下图:


watermark4.png

defer 脚本:
当HTML 文档解析时遇到defer脚本,则在后台加载脚本,文档解析过程不中断,而等文档解析结束之后,defer脚本执行。另外,defer脚本的执行顺序与定义时的位置有关。过程如下图:


watermark5.jpg

async 脚本:
当HTML 文档解析时遇到async脚本,则在后台加载脚本,文档解析时不中断。脚本加载完成后,文档停止解析,脚本执行,执行结束后文档继续解析。过程如下图:


watermark6.jpg

defer 与 DOMContentLoaded
如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。 所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。

async 与 DOMContentLoaded
如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。

7.3 DOMContentLoaded 与load
当HTML文档解析完成后就会触发DOMContentLoaded,而所有资源加载完成之后,load时间才会被触发

另外,我们在jQuery 中常用到的
(document).ready(function(){/*..*/}) 其实监听的就是DOMContentLoaded事件, 而(document).load(function(){/*..*/})监听的事load事件

7.4 使用

document.addEventListener("DOMContentLoaded", function(event){
  console.log("DOM fully loaded and parsed");
})
8. MutationObserver

MutationObserver 是一个内建对象,它观察DOM元素,并在检测到更改时触发回调

8.2 用法

// 选择需要观察变动的节点
const targetNode = document.getElementById('some-id');

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
    // Use traditional 'for loops' for IE 11
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type === 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

8.3 config
config 是一个具有布尔选项的对象,该布尔选项表示“将对哪些更改做出反应”:

childList —— node 的直接子节点的更改,
subtree —— node 的所有后代的更改,
attributes —— node 的特性(attribute),
attributeFilter —— 特性名称数组,只观察选定的特性。
characterData —— 是否观察 node.data(文本内容)

其他几个选项:

attributeOldValue —— 如果为 true,则将特性的旧值和新值都传递给回调(参见下文),否则只传新值(需要 attributes 选项),
characterDataOldValue —— 如果为 true,则将 node.data 的旧值和新值都传递给回调(参见下文),否则只传新值(需要 characterData 选项)。

9. Promise.any

9.1 是什么
Promise.any() 接收一个Promise可迭代对象,只要其中一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的promise都被拒绝/失败),就返回一个失败的promiseAggregateError 类型的实例,它是Error的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.All() 是相反的。

const promise1 = new Promise((resolve, reject)=>{
  setTimeout(reject,100,'promise 1 rejected');
});
const promise2 = new Promise((resolve, reject)=>{
  setTimeout(resolve, 400, 'promise 2 resolved at 400ms')
})
const promise3 = new Promise((resolve, reject)=>{
  setTimeout(resolve, 700, 'promise 3 resolved at 800ms')
})

(async=>{
  try{
    let value = await Promise.any([promise1, promise2, promise3]);
    console.log(value);
  } catch(error){
    console.log(error)
  }
})();
// promise 2 resolved at 400 ms
上一篇下一篇

猜你喜欢

热点阅读