对 this 对象的了解
this 是执行上下文中的一个属性,它指向最初一次调用这个办法的对象。在理论开发中,this 的指向能够通过四种调用模式来判断。
- 第一种是 函数调用模式,当一个函数不是一个对象的属性时,间接作为函数来调用时,this 指向全局对象。
- 第二种是 办法调用模式,如果一个函数作为一个对象的办法来调用时,this 指向这个对象。
- 第三种是 结构器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply、call 和 bind 调用模式,这三个办法都能够显示的指定调用函数的 this 指向。其中 apply 办法接管两个参数:一个是 this 绑定的对象,一个是参数数组。call 办法接管的参数,第一个是 this 绑定的对象,前面的其余参数是传入函数执行的参数。也就是说,在应用 call() 办法时,传递给函数的参数必须一一列举进去。bind 办法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了应用 new 时会被扭转,其余状况下都不会扭转。
这四种形式,应用结构器调用模式的优先级最高,而后是 apply、call 和 bind 调用模式,而后是办法调用模式,而后是函数调用模式。
说一下 HTTP 3.0
HTTP/ 3 基于 UDP 协定实现了相似于 TCP 的多路复用数据流、传输可靠性等性能,这套性能被称为 QUIC 协定。
- 流量管制、传输可靠性性能:QUIC 在 UDP 的根底上减少了一层来保障数据传输可靠性,它提供了数据包重传、拥塞管制、以及其余一些 TCP 中的个性。
- 集成 TLS 加密性能:目前 QUIC 应用 TLS1.3,缩小了握手所破费的 RTT 数。
- 多路复用:同一物理连贯上能够有多个独立的逻辑数据流,实现了数据流的独自传输,解决了 TCP 的队头阻塞问题。
- 疾速握手:因为基于 UDP,能够实现应用 0 ~ 1 个 RTT 来建设连贯。
原型 / 原型链
__proto__
和 prototype 关系 :__proto__
和constructor
是 对象 独有的。2️⃣prototype
属性是 函数 独有的
在 js 中咱们是应用构造函数来新建一个对象的,每一个构造函数的外部都有一个 prototype 属性值,这个属性值是一个对象,这个对象蕴含了能够由该构造函数的所有实例共享的属性和办法。当咱们应用构造函数新建一个对象后,在这个对象的外部将蕴含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说咱们是不应该可能获取到这个值的,然而当初浏览器中都实现了 proto 属性来让咱们拜访这个属性,然而咱们最好不要应用这个属性,因为它不是标准中规定的。ES5 中新增了一个
Object.getPrototypeOf()
办法,咱们能够通过这个办法来获取对象的原型。
当咱们拜访一个对象的属性时,如果这个对象外部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有本人的原型,于是就这样始终找上来,也就是原型链的概念。原型链的止境一般来说都是 Object.prototype
所以这就是咱们新建的对象为什么可能应用 toString()
等办法的起因。
特点:JavaScript 对象是通过援用来传递的,咱们创立的每个新对象实体中并没有一份属于本人的原型正本。当咱们批改原型时,与 之相干的对象也会继承这一扭转
- 原型 (
prototype
): 一个简略的对象,用于实现对象的 属性继承。能够简略的了解成对象的爹。在Firefox
和Chrome
中,每个JavaScript
对象中都蕴含一个__proto__
(非标准) 的属性指向它爹 (该对象的原型),可obj.__proto__
进行拜访。 - 构造函数: 能够通过
new
来 新建一个对象 的函数。 - 实例: 通过构造函数和
new
创立进去的对象,便是实例。实例通过__proto__
指向原型,通过constructor
指向构造函数。
以
Object
为例,咱们罕用的Object
便是一个构造函数,因而咱们能够通过它构建实例。
// 实例
const instance = new Object()
则此时,实例为
instance
, 构造函数为Object
,咱们晓得,构造函数领有一个prototype
的属性指向原型,因而原型为:
// 原型
const prototype = Object.prototype
这里咱们能够来看出三者的关系:
实例.__proto__ === 原型
原型.constructor === 构造函数
构造函数.prototype === 原型
// 这条线其实是是基于原型进行获取的,能够了解成一条基于原型的映射线
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
实例.constructor === 构造函数
原型链
原型链是由原型对象组成,每个对象都有
__proto__
属性,指向了创立该对象的构造函数的原型,__proto__
将对象连接起来组成了原型链。是一个用来实现继承和共享属性的无限的对象链
- 属性查找机制: 当查找对象的属性时,如果实例对象本身不存在该属性,则沿着原型链往上一级查找,找到时则输入,不存在时,则持续沿着原型链往上一级查找,直至最顶级的原型对象
Object.prototype
,如还是没找到,则输入undefined
; - 属性批改机制: 只会批改实例对象自身的属性,如果不存在,则进行增加该属性,如果须要批改原型的属性时,则能够用:
b.prototype.x = 2
;然而这样会造成所有继承于该对象的实例的属性产生扭转。
js 获取原型的办法
p.proto
p.constructor.prototype
Object.getPrototypeOf(p)
总结
- 每个函数都有
prototype
属性,除了Function.prototype.bind()
,该属性指向原型。 - 每个对象都有
__proto__
属性,指向了创立该对象的构造函数的原型。其实这个属性指向了[[prototype]]
,然而[[prototype]]
是外部属性,咱们并不能拜访到,所以应用_proto_
来拜访。 - 对象能够通过
__proto__
来寻找不属于该对象的属性,__proto__
将对象连接起来组成了原型链。
为什么 0.1+0.2 ! == 0.3,如何让其相等
在开发过程中遇到相似这样的问题:
let n1 = 0.1, n2 = 0.2
console.log(n1 + n2) // 0.30000000000000004
这里失去的不是想要的后果,要想等于 0.3,就要把它进行转化:
(n1 + n2).toFixed(2) // 留神,toFixed 为四舍五入
toFixed(num)
办法可把 Number 四舍五入为指定小数位数的数字。那为什么会呈现这样的后果呢?
计算机是通过二进制的形式存储数据的,所以计算机计算 0.1+0.2 的时候,实际上是计算的两个数的二进制的和。0.1 的二进制是0.0001100110011001100...
(1100 循环),0.2 的二进制是:0.00110011001100...
(1100 循环),这两个数的二进制都是有限循环的数。那 JavaScript 是如何解决有限循环的二进制小数呢?
个别咱们认为数字包含整数和小数,然而在 JavaScript 中只有一种数字类型:Number,它的实现遵循 IEEE 754 规范,应用 64 位固定长度来示意,也就是规范的 double 双精度浮点数。在二进制迷信表示法中,双精度浮点数的小数局部最多只能保留 52 位,再加上后面的 1,其实就是保留 53 位有效数字,残余的须要舍去,听从“0 舍 1 入”的准则。
依据这个准则,0.1 和 0.2 的二进制数相加,再转化为十进制数就是:0.30000000000000004
。
上面看一下 双精度数是如何保留 的:
- 第一局部(蓝色):用来存储符号位(sign),用来辨别正负数,0 示意负数,占用 1 位
- 第二局部(绿色):用来存储指数(exponent),占用 11 位
- 第三局部(红色):用来存储小数(fraction),占用 52 位
对于 0.1,它的二进制为:
0.00011001100110011001100110011001100110011001100110011001 10011...
转为迷信计数法(迷信计数法的后果就是浮点数):
1.1001100110011001100110011001100110011001100110011001*2^-4
能够看出 0.1 的符号位为 0,指数位为 -4,小数位为:
1001100110011001100110011001100110011001100110011001
那么问题又来了,指数位是正数,该如何保留 呢?
IEEE 标准规定了一个偏移量,对于指数局部,每次都加这个偏移量进行保留,这样即便指数是正数,那么加上这个偏移量也就是负数了。因为 JavaScript 的数字是双精度数,这里就以双精度数为例,它的指数局部为 11 位,能示意的范畴就是 0~2047,IEEE 固定 双精度数的偏移量为 1023。
- 当指数位不全是 0 也不全是 1 时(规格化的数值),IEEE 规定,阶码计算公式为 e-Bias。此时 e 最小值是 1,则 1 -1023= -1022,e 最大值是 2046,则 2046-1023=1023,能够看到,这种状况下取值范畴是
-1022~1013
。 - 当指数位全副是 0 的时候(非规格化的数值),IEEE 规定,阶码的计算公式为 1 -Bias,即 1 -1023= -1022。
- 当指数位全副是 1 的时候(非凡值),IEEE 规定这个浮点数可用来示意 3 个非凡值,别离是正无穷,负无穷,NaN。具体的,小数位不为 0 的时候示意 NaN;小数位为 0 时,当符号位 s = 0 时示意正无穷,s= 1 时候示意负无穷。
对于下面的 0.1 的指数位为 -4,-4+1023 = 1019 转化为二进制就是:1111111011
.
所以,0.1 示意为:
0 1111111011 1001100110011001100110011001100110011001100110011001
说了这么多,是时候该最开始的问题了,如何实现 0.1+0.2=0.3 呢?
对于这个问题,一个间接的解决办法就是设置一个误差范畴,通常称为“机器精度”。对 JavaScript 来说,这个值通常为 2 -52,在 ES6 中,提供了 Number.EPSILON
属性,而它的值就是 2 -52,只有判断 0.1+0.2-0.3
是否小于Number.EPSILON
,如果小于,就能够判断为 0.1+0.2 ===0.3
function numberepsilon(arg1,arg2){return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true
escape、encodeURI、encodeURIComponent 的区别
- encodeURI 是对整个 URI 进行本义,将 URI 中的非法字符转换为非法字符,所以对于一些在 URI 中有非凡意义的字符不会进行本义。
- encodeURIComponent 是对 URI 的组成部分进行本义,所以一些特殊字符也会失去本义。
- escape 和 encodeURI 的作用雷同,不过它们对于 unicode 编码为 0xff 之外字符的时候会有区别,escape 是间接在字符的 unicode 编码前加上 %u,而 encodeURI 首先会将字符转换为 UTF-8 的格局,再在每个字节前加上 %。
什么是物理像素,逻辑像素和像素密度,为什么在挪动端开发时须要用到 @3x, @2x 这种图片?
以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV 元素宽度为 414px,这个 DIV 就会填满手机的宽度;
而如果有一把尺子来理论测量这部手机的物理像素,理论为 1242*2688 物理像素;通过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素 = 3 个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。
对于图片来说,为了保障其不失真,1 个图片像素至多要对应一个物理像素,如果原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片能力保障 1 个物理像素至多对应一个图片像素,能力不失真。当然,也能够针对所有屏幕,都只提供最高清图片。尽管低密度屏幕用不到那么多图片像素,而且会因为下载多余的像素造成带宽节约和下载提早,但从后果上说能保障图片在所有屏幕上都不会失真。
还能够应用 CSS 媒体查问来判断不同的像素密度,从而抉择不同的图片:
my-image {background: (low.png); }
@media only screen and (min-device-pixel-ratio: 1.5) {#my-image { background: (high.png); }
}
参考 前端进阶面试题具体解答
对line-height 的了解及其赋值形式
(1)line-height 的概念:
- line-height 指一行文本的高度,蕴含了字间距,实际上是下一行基线到上一行基线间隔;
- 如果一个标签没有定义 height 属性,那么其最终体现的高度由 line-height 决定;
- 一个容器没有设置高度,那么撑开容器高度的是 line-height,而不是容器内的文本内容;
- 把 line-height 值设置为 height 一样大小的值能够实现单行文字的垂直居中;
- line-height 和 height 都能撑开一个高度;
(2)line-height 的赋值形式:
- 带单位:px 是固定值,而 em 会参考父元素 font-size 值计算本身的行高
- 纯数字:会把比例传递给后辈。例如,父级行高为 1.5,子元素字体为 18px,则子元素行高为 1.5 * 18 = 27px
- 百分比:将计算后的值传递给后辈
display:inline-block 什么时候会显示间隙?
- 有空格时会有间隙,能够删除空格解决;
margin
正值时,能够让margin
应用负值解决;- 应用
font-size
时,可通过设置font-size:0
、letter-spacing
、word-spacing
解决;
for…in 和 for…of 的区别
for…of 是 ES6 新增的遍历形式,容许遍历一个含有 iterator 接口的数据结构(数组、对象等)并且返回各项的值,和 ES3 中的 for…in 的区别如下
- for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
- for… in 会遍历对象的整个原型链,性能十分差不举荐应用,而 for … of 只遍历以后对象不会遍历原型链;
- 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包含原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for…in 循环次要是为了遍历对象而生,不适用于遍历数组;for…of 循环能够用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
call() 和 apply() 的区别?
它们的作用截然不同,区别仅在于传入参数的模式的不同。
- apply 承受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的汇合,这个汇合能够为数组,也能够为类数组,apply 办法把这个汇合中的元素作为参数传递给被调用的函数。
- call 传入的参数数量不固定,跟 apply 雷同的是,第一个参数也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被顺次传入函数。
Promise 的根本用法
(1)创立 Promise 对象
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已胜利)和 rejected(已失败)。
Promise 构造函数承受一个函数作为参数,该函数的两个参数别离是 resolve
和reject
。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作胜利 */){resolve(value);
} else {reject(error);
}
});
个别状况下都会应用 new Promise()
来创立 promise 对象,然而也能够应用 promise.resolve
和promise.reject
这两个办法:
- Promise.resolve
Promise.resolve(value)
的返回值也是一个 promise 对象,能够对返回值进行.then 调用,代码如下:
Promise.resolve(11).then(function(value){console.log(value); // 打印出 11
});
resolve(11)
代码中,会让 promise 对象进入确定 (resolve
状态),并将参数 11
传递给前面的 then
所指定的onFulfilled
函数;
创立 promise 对象能够应用 new Promise
的模式创建对象,也能够应用 Promise.resolve(value)
的模式创立 promise 对象;
- Promise.reject
Promise.reject
也是 new Promise
的快捷模式,也创立一个 promise 对象。代码如下:
Promise.reject(new Error(“我错了,请原谅俺!!”));
就是上面的代码 new Promise 的简略模式:
new Promise(function(resolve,reject){reject(new Error("我错了!"));
});
上面是应用 resolve 办法和 reject 办法:
function testPromise(ready) {return new Promise(function(resolve,reject){if(ready) {resolve("hello world");
}else {reject("No thanks");
}
});
};
// 办法调用
testPromise(true).then(function(msg){console.log(msg);
},function(error){console.log(error);
});
下面的代码的含意是给 testPromise
办法传递一个参数,返回一个 promise 对象,如果为 true
的话,那么调用 promise 对象中的 resolve()
办法,并且把其中的参数传递给前面的 then
第一个函数内,因而打印出“hello world
”, 如果为 false
的话,会调用 promise 对象中的 reject()
办法,则会进入 then
的第二个函数内,会打印No thanks
;
(2)Promise 办法
Promise 有五个罕用的办法:then()、catch()、all()、race()、finally。上面就来看一下这些办法。
- then()
当 Promise 执行的内容合乎胜利条件时,调用 resolve
函数,失败就调用 reject
函数。Promise 创立完了,那该如何调用呢?
promise.then(function(value) {// success}, function(error) {// failure});
then
办法能够承受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved
时调用,第二个回调函数是 Promise 对象的状态变为 rejected
时调用。其中第二个参数能够省略。then
办法返回的是一个新的 Promise 实例(不是原来那个 Promise 实例)。因而能够采纳链式写法,即 then
办法前面再调用另一个 then 办法。
当要写有程序的异步事件时,须要串行时,能够这样写:
let promise = new Promise((resolve,reject)=>{ajax('first').success(function(res){resolve(res);
})
})
promise.then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
})
})
}).then(res=>{return new Promise((resovle,reject)=>{ajax('second').success(function(res){resolve(res)
})
})
}).then(res=>{})
那当要写的事件没有程序或者关系时,还如何写呢?能够应用all
办法来解决。
2. catch()
Promise 对象除了有 then 办法,还有一个 catch 办法,该办法相当于 then
办法的第二个参数,指向 reject
的回调函数。不过 catch
办法还有一个作用,就是在执行 resolve
回调函数时,如果呈现谬误,抛出异样,不会进行运行,而是进入 catch
办法中。
p.then((data) => {console.log('resolved',data);
},(err) => {console.log('rejected',err);
}
);
p.then((data) => {console.log('resolved',data);
}).catch((err) => {console.log('rejected',err);
});
3. all()
all
办法能够实现并行任务,它接管一个数组,数组的每一项都是一个 promise
对象。当数组中所有的 promise
的状态都达到 resolved
的时候,all
办法的状态就会变成 resolved
,如果有一个状态变成了rejected
,那么all
办法的状态就会变成rejected
。
javascript
let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(1);
},2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
},1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
},3000)
});
Promise.all([promise1,promise2,promise3]).then(res=>{console.log(res);
// 后果为:[1,2,3]
})
调用 all
办法时的后果胜利的时候是回调函数的参数也是一个数组,这个数组按程序保留着每一个 promise 对象 resolve
执行时的值。
(4)race()
race
办法和 all
一样,承受的参数是一个每项都是 promise
的数组,然而与 all
不同的是,当最先执行完的事件执行完之后,就间接返回该 promise
对象的值。如果第一个 promise
对象状态变成 resolved
,那本身的状态变成了resolved
;反之第一个promise
变成rejected
,那本身状态就会变成rejected
。
let promise1 = new Promise((resolve,reject)=>{setTimeout(()=>{reject(1);
},2000)
});
let promise2 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(2);
},1000)
});
let promise3 = new Promise((resolve,reject)=>{setTimeout(()=>{resolve(3);
},3000)
});
Promise.race([promise1,promise2,promise3]).then(res=>{console.log(res);
// 后果:2
},rej=>{console.log(rej)};
)
那么 race
办法有什么理论作用呢?当要做一件事,超过多长时间就不做了,能够用这个办法来解决:
Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})
5. finally()
finally
办法用于指定不论 Promise 对象最初状态如何,都会执行的操作。该办法是 ES2018 引入规范的。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
下面代码中,不论 promise
最初的状态,在执行完 then
或catch
指定的回调函数当前,都会执行 finally
办法指定的回调函数。
上面是一个例子,服务器应用 Promise 解决申请,而后应用 finally
办法关掉服务器。
server.listen(port)
.then(function () {// ...})
.finally(server.stop);
finally
办法的回调函数不承受任何参数,这意味着没有方法晓得,后面的 Promise 状态到底是 fulfilled
还是 rejected
。这表明,finally
办法外面的操作,应该是与状态无关的,不依赖于 Promise 的执行后果。finally
实质上是 then
办法的特例:
promise
.finally(() => {// 语句});
// 等同于
promise
.then(
result => {
// 语句
return result;
},
error => {
// 语句
throw error;
}
);
下面代码中,如果不应用 finally
办法,同样的语句须要为胜利和失败两种状况各写一次。有了 finally
办法,则只须要写一次。
margin 和 padding 的应用场景
- 须要在 border 外侧增加空白,且空白处不须要背景(色)时,应用 margin;
- 须要在 border 内测增加空白,且空白处须要背景(色)时,应用 padding。
伪元素和伪类的区别和作用?
- 伪元素:在内容元素的前后插入额定的元素或款式,然而这些元素实际上并不在文档中生成。它们只在内部显示可见,但不会在文档的源代码中找到它们,因而,称为“伪”元素。例如:
p::before {content:"第一章:";}
p::after {content:"Hot!";}
p::first-line {background:red;}
p::first-letter {font-size:30px;}
- 伪类:将非凡的成果增加到特定选择器上。它是已有元素上增加类别的,不会产生新的元素。例如:
a:hover {color: #FF00FF}
p:first-child {color: red}
总结: 伪类是通过在元素选择器上加⼊伪类扭转元素状态,⽽伪元素通过对元素的操作进⾏对元素的扭转。
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(构造函数, 初始化参数);
暗藏元素的办法有哪些
- display: none:渲染树不会蕴含该渲染对象,因而该元素不会在页面中占据地位,也不会响应绑定的监听事件。
- visibility: hidden:元素在页面中仍占据空间,然而不会响应绑定的监听事件。
- opacity: 0:将元素的透明度设置为 0,以此来实现元素的暗藏。元素在页面中依然占据空间,并且可能响应元素绑定的监听事件。
- position: absolute:通过应用相对定位将元素移除可视区域内,以此来实现元素的暗藏。
- z-index: 负值:来使其余元素遮盖住该元素,以此来实现暗藏。
- clip/clip-path:应用元素裁剪的办法来实现元素的暗藏,这种办法下,元素仍在页面中占据地位,然而不会响应绑定的监听事件。
- transform: scale(0,0):将元素缩放为 0,来实现元素的暗藏。这种办法下,元素仍在页面中占据地位,然而不会响应绑定的监听事件。
transition 和 animation 的区别
- transition 是适度属性,强调适度,它的实现须要触发一个事件(比方鼠标挪动下来,焦点,点击等)才执行动画。它相似于 flash 的补间动画,设置一个开始关键帧,一个完结关键帧。
- animation 是动画属性,它的实现不须要触发事件,设定好工夫之后能够本人执行,且能够循环一个动画。它也相似于 flash 的补间动画,然而它能够设置多个关键帧(用 @keyframe 定义)实现动画。
iframe 有那些长处和毛病?
iframe 元素会创立蕴含另外一个文档的内联框架(即行内框架)。
长处:
- 用来加载速度较慢的内容(如广告)
- 能够使脚本能够并行下载
- 能够实现跨子域通信
毛病:
- iframe 会阻塞主页面的 onload 事件
- 无奈被一些搜索引擎索辨认
- 会产生很多页面,不容易治理
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
代码,能够利用到老我的项目中
实现一个宽高自适应的正方形
- 利用 vw 来实现:
.square {
width: 10%;
height: 10vw;
background: tomato;
}
- 利用元素的 margin/padding 百分比是绝对父元素 width 的性质来实现:
.square {
width: 20%;
height: 0;
padding-top: 20%;
background: orange;
}
- 利用子元素的 margin-top 的值来实现:
.square {
width: 30%;
overflow: hidden;
background: yellow;
}
.square::after {
content: '';
display: block;
margin-top: 100%;
}
为什么须要浏览器缓存?
对于浏览器的缓存,次要针对的是前端的动态资源,最好的成果就是,在发动申请之后,拉取相应的动态资源,并保留在本地。如果服务器的动态资源没有更新,那么在下次申请的时候,就间接从本地读取即可,如果服务器的动态资源曾经更新,那么咱们再次申请的时候,就到服务器拉取新的资源,并保留在本地。这样就大大的缩小了申请的次数,进步了网站的性能。这就要用到浏览器的缓存策略了。
所谓的 浏览器缓存 指的是浏览器将用户申请过的动态资源,存储到电脑本地磁盘中,当浏览器再次拜访时,就能够间接从本地加载,不须要再去服务端申请了。
应用浏览器缓存,有以下长处:
- 缩小了服务器的累赘,进步了网站的性能
- 放慢了客户端网页的加载速度
- 缩小了多余网络数据传输