img的srcset属性的作⽤?
响应式页面中常常用到依据屏幕密度设置不同的图片。这时就用到了 img 标签的srcset属性。srcset属性用于设置不同屏幕密度下,img 会主动加载不同的图片。用法如下:
<img src="image-128.png" srcset="image-256.png 2x" />
应用下面的代码,就能实现在屏幕密度为1x的状况下加载image-128.png, 屏幕密度为2x时加载image-256.png。
依照下面的实现,不同的屏幕密度都要设置图片地址,目前的屏幕密度有1x,2x,3x,4x四种,如果每一个图片都设置4张图片,加载就会很慢。所以就有了新的srcset规范。代码如下:
<img src="image-128.png" srcset="image-128.png 128w, image-256.png 256w, image-512.png 512w" sizes="(max-width: 360px) 340px, 128px" />
其中srcset指定图片的地址和对应的图片品质。sizes用来设置图片的尺寸零界点。对于 srcset 中的 w 单位,能够了解成图片品质。如果可视区域小于这个品质的值,就能够应用。浏览器会主动抉择一个最小的可用图片。
sizes语法如下:
sizes="[media query] [length], [media query] [length] ... "
sizes就是指默认显示128px, 如果视区宽度大于360px, 则显示340px。
左右居中计划
- 行内元素:
text-align: center
- 定宽块状元素: 左右
margin
值为auto
- 不定宽块状元素:
table
布局,position + transform
/* 计划1 */.wrap { text-align: center}.center { display: inline; /* or */ /* display: inline-block; */}/* 计划2 */.center { width: 100px; margin: 0 auto;}/* 计划2 */.wrap { position: relative;}.center { position: absulote; left: 50%; transform: translateX(-50%);}
Loader和Plugin 有什么区别
Loader:直译为"加载器"。Webpack将所有文件视为模块,然而webpack原生是只能解析js文件,如果想将其余文件也打包的话,就会用到loader
。 所以Loader的作用是让webpack领有了加载和解析非JavaScript文件的能力。 Plugin:直译为"插件"。Plugin能够扩大webpack的性能,让webpack具备更多的灵活性。 在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。
什么是 JavaScript 中的包装类型?
在 JavaScript 中,根本类型是没有属性和办法的,然而为了便于操作根本类型的值,在调用根本类型的属性或办法时 JavaScript 会在后盾隐式地将根本类型的值转换为对象,如:
const a = "abc";a.length; // 3a.toUpperCase(); // "ABC"
在拜访'abc'.length
时,JavaScript 将'abc'
在后盾转换成String('abc')
,而后再拜访其length
属性。
JavaScript也能够应用Object
函数显式地将根本类型转换为包装类型:
var a = 'abc'Object(a) // String {"abc"}
也能够应用valueOf
办法将包装类型倒转成根本类型:
var a = 'abc'var b = Object(a)var c = b.valueOf() // 'abc'
看看如下代码会打印出什么:
var a = new Boolean( false );if (!a) { console.log( "Oops" ); // never runs}
答案是什么都不会打印,因为尽管包裹的根本类型是false
,然而false
被包裹成包装类型后就成了对象,所以其非值为false
,所以循环体中的内容不会运行。
为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组?
arguments
是一个对象,它的属性是从 0 开始顺次递增的数字,还有callee
和length
等属性,与数组类似;然而它却没有数组常见的办法属性,如forEach
, reduce
等,所以叫它们类数组。
要遍历类数组,有三个办法:
(1)将数组的办法利用到类数组上,这时候就能够应用call
和apply
办法,如:
function foo(){ Array.prototype.forEach.call(arguments, a => console.log(a))}
(2)应用Array.from办法将类数组转化成数组:
function foo(){ const arrArgs = Array.from(arguments) arrArgs.forEach(a => console.log(a))}
(3)应用开展运算符将类数组转化成数组
function foo(){ const arrArgs = [...arguments] arrArgs.forEach(a => console.log(a)) }
代码输入后果
function runAsync(x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000) ); return p;}function runReject(x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x) ); return p;}Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)]) .then(res => console.log("result: ", res)) .catch(err => console.log(err));
输入后果如下:
0Error: 0123
能够看到在catch捕捉到第一个谬误之后,前面的代码还不执行,不过不会再被捕捉了。
留神:all
和race
传入的数组中如果有会抛出异样的异步工作,那么只有最先抛出的谬误会被捕捉,并且是被then的第二个参数或者前面的catch捕捉;但并不会影响数组中其它的异步工作的执行。
参考 前端进阶面试题具体解答
事件是什么?事件模型?
事件是用户操作网页时产生的交互动作,比方 click/move, 事件除了用户触发的动作外,还能够是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,蕴含了该事件产生时的所有相干信息( event 的属性)以及能够对事件进行的操作( event 的办法)。
事件是用户操作网页时产生的交互动作或者网页自身的一些操作,古代浏览器一共有三种事件模型:
- DOM0 级事件模型,这种模型不会流传,所以没有事件流的概念,然而当初有的浏览器反对以冒泡的形式实现,它能够在网页中间接定义监听函数,也能够通过 js 属性来指定监听函数。所有浏览器都兼容这种形式。间接在dom对象上注册事件名称,就是DOM0写法。
- IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行指标元素绑定的监听事件。而后是事件冒泡阶段,冒泡指的是事件从指标元素冒泡到 document,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过attachEvent 来增加监听函数,能够增加多个监听函数,会按程序顺次执行。
- DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕捉阶段。捕捉指的是事件从 document 始终向下流传到指标元素,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。前面两个阶段和 IE 事件模型的两个阶段雷同。这种事件模型,事件绑定的函数是addEventListener,其中第三个参数能够指定事件是否在捕捉阶段执行。
== 操作符的强制类型转换规定?
对于 ==
来说,如果比照单方的类型不一样,就会进行类型转换。如果比照 x
和 y
是否雷同,就会进行如下判断流程:
- 首先会判断两者类型是否雷同,雷同的话就比拟两者的大小;
- 类型不雷同的话,就会进行类型转换;
- 会先判断是否在比照
null
和undefined
,是的话就会返回true
- 判断两者类型是否为
string
和number
,是的话就会将字符串转换为number
1 == '1' ↓1 == 1
- 判断其中一方是否为
boolean
,是的话就会把boolean
转为number
再进行判断
'1' == true ↓'1' == 1 ↓ 1 == 1
- 判断其中一方是否为
object
且另一方为string
、number
或者symbol
,是的话就会把object
转为原始类型再进行判断
'1' == { name: 'js' } ↓'1' == '[object Object]'
原型批改、重写
function Person(name) { this.name = name}// 批改原型Person.prototype.getName = function() {}var p = new Person('hello')console.log(p.__proto__ === Person.prototype) // trueconsole.log(p.__proto__ === p.constructor.prototype) // true// 重写原型Person.prototype = { getName: function() {}}var p = new Person('hello')console.log(p.__proto__ === Person.prototype) // trueconsole.log(p.__proto__ === p.constructor.prototype) // false
能够看到批改原型的时候p的构造函数不是指向Person了,因为间接给Person的原型对象间接用对象赋值时,它的构造函数指向的了根构造函数Object,所以这时候p.constructor === Object
,而不是p.constructor === Person
。要想成立,就要用constructor指回来:
Person.prototype = { getName: function() {}}var p = new Person('hello')p.constructor = Personconsole.log(p.__proto__ === Person.prototype) // trueconsole.log(p.__proto__ === p.constructor.prototype) // true
对 rest 参数的了解
扩大运算符被用在函数形参上时,它还能够把一个拆散的参数序列整合成一个数组:
function mutiple(...args) { let result = 1; for (var val of args) { result *= val; } return result;}mutiple(1, 2, 3, 4) // 24
这里,传入 mutiple 的是四个拆散的参数,然而如果在 mutiple 函数里尝试输入 args 的值,会发现它是一个数组:
function mutiple(...args) { console.log(args)}mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
这就是 … rest运算符的又一层威力了,它能够把函数的多个入参收敛进一个数组里。这一点常常用于获取函数的多余参数,或者像下面这样解决函数参数个数不确定的状况。
JS 整数是怎么示意的?
- 通过 Number 类型来示意,遵循 IEEE754 规范,通过 64 位来示意一个数字,(1 + 11 + 52),最大平安数字是 Math.pow(2, 53) - 1,对于 16 位十进制。(符号位 + 指数位 + 小数局部无效位)
代码输入后果
function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p}function runReject (x) { const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)) return p}Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]) .then(res => console.log(res)) .catch(err => console.log(err))
输入后果如下:
// 1s后输入13// 2s后输入2Error: 2// 4s后输入4
能够看到。catch捕捉到了第一个谬误,在这道题目中最先的谬误就是runReject(2)
的后果。如果一组异步操作中有一个异样都不会进入.then()
的第一个回调函数参数中。会被.then()
的第二个回调函数捕捉。
new操作符的实现原理
new操作符的执行过程:
(1)首先创立了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性)
(4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。
具体实现:
function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判断参数是否是一个函数 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一个空对象,对象的原型为构造函数的 prototype 对象 newObject = Object.create(constructor.prototype); // 将 this 指向新建对象,并执行函数 result = constructor.apply(newObject, arguments); // 判断返回对象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判断返回后果 return flag ? result : newObject;}// 应用办法objectFactory(构造函数, 初始化参数);
常见的DOM操作有哪些
1)DOM 节点的获取
DOM 节点的获取的API及应用:
getElementById // 依照 id 查问getElementsByTagName // 依照标签名查问getElementsByClassName // 依照类名查问querySelectorAll // 依照 css 选择器查问// 依照 id 查问var imooc = document.getElementById('imooc') // 查问到 id 为 imooc 的元素// 依照标签名查问var pList = document.getElementsByTagName('p') // 查问到标签为 p 的汇合console.log(divList.length)console.log(divList[0])// 依照类名查问var moocList = document.getElementsByClassName('mooc') // 查问到类名为 mooc 的汇合// 依照 css 选择器查问var pList = document.querySelectorAll('.mooc') // 查问到类名为 mooc 的汇合
2)DOM 节点的创立
创立一个新节点,并把它增加到指定节点的前面。 已知的 HTML 构造如下:
<html> <head> <title>DEMO</title> </head> <body> <div id="container"> <h1 id="title">我是题目</h1> </div> </body></html>
要求增加一个有内容的 span 节点到 id 为 title 的节点前面,做法就是:
// 首先获取父节点var container = document.getElementById('container')// 创立新节点var targetSpan = document.createElement('span')// 设置 span 节点的内容targetSpan.innerHTML = 'hello world'// 把新创建的元素塞进父节点里去container.appendChild(targetSpan)
3)DOM 节点的删除
删除指定的 DOM 节点, 已知的 HTML 构造如下:
<html> <head> <title>DEMO</title> </head> <body> <div id="container"> <h1 id="title">我是题目</h1> </div> </body></html>
须要删除 id 为 title 的元素,做法是:
// 获取指标元素的父元素var container = document.getElementById('container')// 获取指标元素var targetNode = document.getElementById('title')// 删除指标元素container.removeChild(targetNode)
或者通过子节点数组来实现删除:
// 获取指标元素的父元素var container = document.getElementById('container')// 获取指标元素var targetNode = container.childNodes[1]// 删除指标元素container.removeChild(targetNode)
4)批改 DOM 元素
批改 DOM 元素这个动作能够分很多维度,比如说挪动 DOM 元素的地位,批改 DOM 元素的属性等。
将指定的两个 DOM 元素替换地位, 已知的 HTML 构造如下:
<html> <head> <title>DEMO</title> </head> <body> <div id="container"> <h1 id="title">我是题目</h1> <p id="content">我是内容</p> </div> </body></html>
当初须要调换 title 和 content 的地位,能够思考 insertBefore 或者 appendChild:
// 获取父元素var container = document.getElementById('container') // 获取两个须要被替换的元素var title = document.getElementById('title')var content = document.getElementById('content')// 替换两个元素,把 content 置于 title 后面container.insertBefore(content, title)
Promise.resolve
Promise.resolve = function(value) { // 1.如果 value 参数是一个 Promise 对象,则一成不变返回该对象 if(value instanceof Promise) return value; // 2.如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的then办法 if(typeof value === "object" && 'then' in value) { return new Promise((resolve, reject) => { value.then(resolve, reject); }); } // 3.否则返回一个新的 Promise 对象,状态为 fulfilled return new Promise(resolve => resolve(value));}
JavaScript有哪些内置对象
全局的对象( global objects )或称规范内置对象,不要和 "全局对象(global object)" 混同。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其余对象能够由用户的脚本创立或由宿主程序提供。
规范内置对象的分类:
(1)值属性,这些全局属性返回一个简略值,这些值没有本人的属性和办法。例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数能够间接调用,不须要在调用时指定所属对象,执行完结后会将后果间接返回给调用者。例如 eval()、parseFloat()、parseInt() 等
(3)根本对象,根本对象是定义或应用其余对象的根底。根本对象包含个别对象、函数对象和谬误对象。例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来示意数字、日期和执行数学计算的对象。例如 Number、Math、Date
(5)字符串,用来示意和操作字符串的对象。例如 String、RegExp
(6)可索引的汇合对象,这些对象示意依照索引值来排序的数据汇合,包含数组和类型数组,以及类数组构造的对象。例如 Array
(7)应用键的汇合对象,这些汇合对象在存储数据时会应用到键,反对依照插入程序来迭代元素。
例如 Map、Set、WeakMap、WeakSet
(8)矢量汇合,SIMD 矢量汇合中的数据会被组织为一个数据序列。
例如 SIMD 等
(9)结构化数据,这些对象用来示意和操作结构化的缓冲区数据,或应用 JSON 编码的数据。例如 JSON 等
(10)管制形象对象
例如 Promise、Generator 等
(11)反射。例如 Reflect、Proxy
(12)国际化,为了反对多语言解决而退出 ECMAScript 的对象。例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其余。例如 arguments
总结: js 中的内置对象次要指的是在程序执行前存在全局作用域里的由 js 定义的一些全局值属性、函数和用来实例化其余对象的构造函数对象。个别常常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。
JavaScript 类数组对象的定义?
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,还有一个函数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
(1)通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
(2)通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
(3)通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
(4)通过 Array.from 办法来实现转换
Array.from(arrayLike);
对类数组对象的了解,如何转化为数组
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,函数参数也能够被看作是类数组对象,因为它含有 length属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
- 通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
- 通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
- 通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
- 通过 Array.from 办法来实现转换
Array.from(arrayLike);
说一下JSON.stringify有什么毛病?
1.如果obj外面有工夫对象,则JSON.stringify后再JSON.parse的后果,工夫将只是字符串的模式,而不是对象的模式2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的后果将只失去空对象;3、如果obj里有函数,undefined,则序列化的后果会把函数或 undefined失落;4、如果obj里有NaN、Infinity和-Infinity,则序列化的后果会变成null5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则应用JSON.parse(JSON.stringify(obj))深拷贝后,会抛弃对象的constructor;6、如果对象中存在循环援用的状况也无奈正确实现深拷贝;
CSS预处理器/后处理器是什么?为什么要应用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译sass
或者less
,减少了css
代码的复用性。层级,mixin
, 变量,循环, 函数等对编写以及开发UI组件都极为不便。
后处理器, 如: postCss
,通常是在实现的样式表中依据css
标准解决css
,让其更加无效。目前最常做的是给css
属性增加浏览器公有前缀,实现跨浏览器兼容性的问题。
css
预处理器为css
减少一些编程个性,无需思考浏览器的兼容问题,能够在CSS
中应用变量,简略的逻辑程序,函数等在编程语言中的一些根本的性能,能够让css
更加的简洁,减少适应性以及可读性,可维护性等。
其它css
预处理器语言:Sass(Scss)
, Less
, Stylus
, Turbine
, Swithch css
, CSS Cacheer
, DT Css
。
应用起因:
- 构造清晰, 便于扩大
- 能够很不便的屏蔽浏览器公有语法的差别
- 能够轻松实现多重继承
- 完满的兼容了
CSS
代码,能够利用到老我的项目中