可参考:juejin.im/post/5ac43e… 可参考:github.com/ltadpoles/w…javascript
<p style="width: 300px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;">
复制代码
width:加宽度属性来兼容部分浏览器php
white-space:设置如何处理元素中的空白。详细见:developer.mozilla.org/zh-CN/docs/…css
text-overflow:ellipsis:单行文本的溢出显示省略号html
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
复制代码
因使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;前端
注:-webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它须要组合其余的WebKit属性。java
常见结合属性:ios
PS:字符串中包含'\r\n' 需添加white-space: pre-line;
渲染才能换行展现nginx
background-image: linear-gradient(-90deg, #ff62c5 0%, #ff5687 100%);
box-shadow: 0 1px 4px 0 rgba(255, 100, 145, 0.7);
复制代码
linear-gradient(45deg, blue, red);/* 渐变轴为45度,从蓝色渐变到红色 */git
linear-gradient(to left top, blue, red);/* 从右下到左上、从蓝色渐变到红色 */github
linear-gradient(0deg, blue, green 40%, red);/* 从下到上,从蓝色开始渐变、到高度40%位置是绿色渐变开始、最后以红色结束 */
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */
当鼠标放置于图片上,图片会迅速变大。图片变大是瞬间实现的。
img{
height:15px;
width:15px;
}
img:hover{
height: 450px;
width: 450px;
}
复制代码
transition的做用在于,指定状态变化所须要的时间。
img{
transition: 1s;
}
复制代码
还能够指定transition适用的属性,好比只适用于height。只有height的变化须要1秒实现,其余变化(主要是width)依然瞬间实现。
img{
transition: 1s height;
}
复制代码
height和width的变是同时进行,跟不指定它们没有差异
img{
transition: 1s height, 1s width;
}
复制代码
让height先发生变化,等结束之后,再让width发生变化。
img{
transition: 1s height, 1s 1s width;
}
复制代码
img{
transition: 1s ease;
}
复制代码
除了ease之外,其余模式还包括
(1)linear:匀速
(2)ease-in:加速
(3)ease-out:减速
(4)cubic-bezier函数:自定义速度模式,cubic-bezier能够使用工具网站来定制。
img{
transition: 1s height cubic-bezier(.83,.97,.05,1.44);
}
复制代码
transition的各项属性 transition的完整写法以下。
img{
transition: 1s 1s height ease;
}
复制代码
这实际上是一个简写形式,能够单独定义成各个属性。
img{
transition-property: height;
transition-duration: 1s;
transition-delay: 1s;
transition-timing-function: ease;
}
复制代码
CSS Animation须要指定动画一个周期持续的时间,以及动画效果的名称。
当鼠标悬停在div元素上时,会产生名为rainbow的动画效果,持续时间为1秒。为此,咱们还须要用keyframes关键字,定义rainbow效果。rainbow效果一共有三个状态,分别为起始(0%)、中点(50%)和结束(100%)。
div:hover {
animation: 1s rainbow;
}
@keyframes rainbow {
0% { background: #c00; }
50% { background: orange; }
100% { background: yellowgreen; }
}
复制代码
默认状况下,动画只播放一次。加入infinite关键字,可让动画无限次播放。
div:hover {
animation: 1s rainbow infinite;
}
复制代码
也能够指定动画具体播放的次数,好比3次。
div:hover {
animation: 1s rainbow 3;
}
复制代码
div:hover {
animation: 1s rainbow forwards; /* forwards表示让动画停留在结束状态 */
}
复制代码
animation-fill-mode还能够使用下列值。
(1)none:默认值,回到动画没开始时的状态。
(2)backwards:让动画回到第一帧的状态。
(3)both: 根据animation-direction轮流应用forwards和backwards规则。
/* 有这样一个动画 */
@keyframes rainbow {
0% { background-color: yellow; }
100% { background: blue; }
}
/* 默认状况animation-direction等于normal */
div:hover {
animation: 1s rainbow 3 normal;
}
复制代码
此外,还能够等于取alternate、reverse、alternate-reverse等值。它们的含义见下图(假定动画连续播放三次)。
animation的各项属性,同transition同样,animation也是一个简写形式。
div:hover {
animation: 1s 1s rainbow linear 3 forwards normal;
}
复制代码
这是一个简写形式,能够分解成各个单独的属性。
div:hover {
animation-name: rainbow;
animation-duration: 1s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode:forwards;
animation-direction: normal;
animation-iteration-count: 3;
}
复制代码
@keyframes rainbow {
0% { background: #c00 }
50% { background: orange }
100% { background: yellowgreen }
}
/* 0%能够用from表明,100%能够用to表明,所以上面的代码等同于下面的形式。 */
@keyframes rainbow {
from { background: #c00 }
50% { background: orange }
to { background: yellowgreen }
}
复制代码
若是省略某个状态,浏览器会自动推算中间状态,因此下面都是合法的写法。
@keyframes rainbow {
50% { background: orange }
to { background: yellowgreen }
}
@keyframes rainbow {
to { background: yellowgreen }
}
/* 甚至,能够把多个状态写在一行。*/
@keyframes pound {
from,to { transform: none; }
50% { transform: scale(1.2); }
}
/* 另一点须要注意的是,浏览器从一个状态向另外一个状态过渡,是平滑过渡。steps函数能够实现分步过渡。*/
div:hover {
animation: 1s rainbow infinite steps(10);
}
复制代码
鼠标没有悬停时,动画状态是暂停;一旦悬停,动画状态改成继续播放。
div {
animation: spin 1s linear infinite;
animation-play-state: paused;
}
div:hover {
animation-play-state: running;
}
复制代码
注意:浏览器前缀,目前,IE 10和Firefox(>= 16)支持没有前缀的animation,而chrome不支持,因此必须使用webkit前缀。
在CSS2以前规范不明确的时候,伪元素和伪类都使用单冒号(:)来表示。
好比 :before :after :hover
而CSS3规范中的要求使用双冒号(::)表示伪元素,以此来区分伪元素和伪类。
上面的例子用CSS3的规范就应该写成 ::before ::after :hover
为了兼容过去的写法,CSS3以前的伪元素仍然能够使用单冒号(:)来表示,浏览器是能够解析的。
好比 :before 和 ::before 均可以被浏览器解析。
可是CSS3以后出现的伪元素必须用双冒号表示,再也不支持单冒号的形式。
也称做Layout,中文叫回流,通常意味着元素的内容、结构、位置或尺寸发生了变化,须要从新计算样式和渲染树,这个过程称为Reflow。
中文重绘,意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只须要应用新样式绘制这个元素就OK了,这个过程称为Repaint。
Reflow的成本比Repaint的成本高得多的多。DOM树里的每一个结点都会有reflow方法,一个结点的reflow颇有可能致使子结点,甚至父点以及同级结点的reflow。
一、Initial,网页初始化的时候。 二、Incremental,一些js在操做DOM树时。 三、Resize,其些元件的尺寸变了。 四、StyleChange,若是CSS的属性发生变化了。
css的盒模型由content(内容)、padding(内边距)、border(边框)、margin(外边距)组成。但盒子的大小由content+padding+border这几部分决定
box-sizing是一个CSS3属性,与盒子模型有着密切联系。即决定元素的宽高如何计算,box-sizing有三个属性:
box-sizing: content-box|border-box|inherit:
标准盒模型(content-box):盒模型的宽高只是内容(content)的宽高
怪异盒模型(border-box):盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高
inherit 指定box-sizing属性的值,应该从父元素继承
浮动的元素是脱离文档标准流的,若是咱们不清楚浮动,那么就会形成父元素高度塌陷,影响页面布局。
清除浮动的方式:
为父元素设置高度
为父元素添加overflow:hidden
overflow:hidden能够触发BFC机制。BFC:块级格式化上下文,建立了 BFC的元素就是一个独立的盒子,它规定了内部如何布局,而且与这个独立盒子里的布局不受外部影响,固然它也不会影响到外面的元素,计算BFC的高度时,浮动元素也参与计算
伪元素
.fix::after {
content:"";
display:block;
clear:both;
}
复制代码
使用伪元素的好处:不增长冗余的DOM节点,符合语义化
link属于XHTML标签,@import彻底是CSS提供的一种方式,只能加载CSS
加载顺序的差异,当一个页面被加载的时候,link引用的CSS会同时被加载,而@import引用的CSS 会等到页面所有被下载完再被加载
兼容性的差异。因为@import是CSS2.1提出的因此老的浏览器不支持,而link标签无此问题
当使用javascript控制dom去改变样式的时候,只能使用link标签,由于@import不是dom能够控制的
offsetWidth/offsetHeight
返回值包含content + padding + border,效果与e.getBoundingClientRect()相同
clientWidth/clientHeight
返回值只包含content + padding,若是有滚动条,也不包含滚动条
scrollWidth/scrollHeight
返回值包含content + padding + 溢出内容的尺寸
6种原始数据类型:
引用类型:Object
详见:developer.mozilla.org/zh-CN/docs/…
typeof操做符:返回一个字符串,表示未经计算的操做数的类型。除了null均可以显示正确的类型。
typeof 操做符对于简单数据类型,返回其自己的数据类型,函数对象返回 function ,其余对象均返回 Object
typeof 1 //'number'
typeof '1' //'string'
typeof undefined //'undefined'
typeof true //'boolean'
typeof Symbol() //'symbol'
typeof b //b没有生命,可是还会显示undefined
typeof [] //'object'
typeof {} //'object'
typeof console.log //'function'
typeof null //'object'
复制代码
null 返回 Object,出现这种状况的缘由是:在js最第一版本中,使用的是32位系统,为了性能考虑使用低位存储了变量的类型信息,000开头表明的是对象,然而null表示为全0,因此将它错误的判断成了object。虽然如今的内部类型判断代码已经改变了,可是这个bug仍是存在的。
instanceof: 用来判断A 是不是 B的实例,表达式为 A instanceof B,返回一个Boolean类型的值 instanceof 检测的是原型,只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪一种类型
let a = [];
a instanceof Array // true
a instanceof Object // true
复制代码
变量a 的 __ proto__ 直接指向Array.prototype,间接指向 Object.prototype,因此按照 instanceof 的判断规则,a 就是Object的实例.针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象自己是否为 Array 类型
constructor: 当一个函数被定义时,JS引擎会为其添加prototype原型,而后再在 prototype上添加一个 constructor 属性,并让其指向该函数的引用
null和undefined是无效的对象,所以是不会有constructor存在的,这两种类型的数据须要经过其余方式来判断
函数的constructor是不稳定的,这个主要体如今自定义对象上,当开发者重写prototype后,原有的constructor引用会丢失,constructor会默认为 Object
function F() {};
var f = new F;
f.constructor == F // true
F.prototype = {a: 1}
var f = new F
f.constructor == F // false
复制代码
在构造函数 F.prototype 没有被重写以前,构造函数 F 就是新建立的对象 f 的数据类型。当 F.prototype 被重写以后,原有的 constructor 引用丢失, 默认为 Object
所以,为了规范开发,在重写对象原型时通常都须要从新给 constructor 赋值,以保证对象实例的类型不被篡改
toString: Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。能够得到变量的正确类型。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(11) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call([]) ; // [object Array]
复制代码
可参考: www.jianshu.com/p/44ba37660…
var obj = {}
复制代码
var obj = new Object()
复制代码
function Person(name, age) {
var o = new Object()
o.name = name;
o.age = age;
o.say = function() {
console.log(name)
}
return o
}
复制代码
缺点: 每次经过Person建立对象的时候,全部的say方法都是同样的,可是却存储了屡次,浪费资源
function Person(name, age) {
this.name = name
this.age = age
this.say = function() {
console.log(name)
}
}
var person = new Person('hello', 18)
复制代码
构造函数模式,隐式地在最后return this
因此在缺乏new的状况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象,能够根据return this 的特性调用call或者apply指定this
function Person() {}
Person.prototype.name = 'hanmeimei';
Person.prototype.say = function() {
alert(this.name);
}
Person.prototype.friends = ['lilei'];
var person = new Person();
复制代码
实现了方法与属性的共享,能够动态添加对象的属性和方法。可是没有办法建立实例本身的属性和方法,也没有办法传递参数
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.say = function() {
console.log(this.name)
}
var person = new Person('hello')
复制代码
参考:www.ruanyifeng.com/blog/2014/0…
修改器方法:详细可参考:juejin.im/post/5c3ede… & juejin.im/post/5c3f01…
访问方法:
迭代方法:详细可参考另外一篇总结:juejin.im/post/5c2c94…
事件捕获:当使用事件捕获时,父级元素先触发,子元素后触发
事件冒泡:当使用事件冒泡时,子级元素先触发,父元素后触发
W3C:任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达事件源,再从事件源向上进行事件捕获
标准浏览器:addEventListener("click","doSomething","true")方法,若第三参数为true则采用事件捕获,若为false,则采用事件冒泡
IE浏览器只支持事件冒泡,不支持事件捕获,因此它不支持addEventListener("click","doSomething","true")方法,因此ie浏览器使用ele.attachEvent("onclick",doSomething)
【事件传播的阻止方法】
在W3C中,使用event.stopPropagation()方法
在IE下使用event.cancelBubble = true方法
【阻止默认行为】
在W3c中,使用event.preventDefault()方法
在IE下 使用event.returnValue = false; 或 return false;
原理:利用事件冒泡机制实现的
优势:
this
关键字和箭头函数参考另外一篇总结:juejin.im/post/5ca324…
简单来讲,闭包就是可以读取其余函数内部变量的函数
function Person() {
var name = 'hello'
function say () {
console.log(name)
}
return say()
}
Person() // hello
复制代码
因为 JavaScript 特殊的做用域,函数外部没法直接读取内部的变量,内部能够直接读取外部的变量,从而就产生了闭包的概念
用途:
注意点:
循环定时器例题:juejin.im/post/58f1fa…
类似之处:
都是用来改变函数的 this 对象的指向的。
第一个参数都是 this 要指向的对象。
均可以利用后续参数传参。
区别:
call 接受函数传参方式为:fn.call(this, 1, 2, 3)
apply 接受函数传参方式为:fn.apply(this,[1, 2, 3])
bind 将函数绑定至某个对象,当在f()上调用bind()方法并传入一个对象o做为参数,这个方法会返回一个新的函数。以函数调用的方式调用新的函数将会把原始的函数f()当作新对象o的方法来调用。传入新函数的任何实参都将传入原始函数f()。
let a = {
value: 1
}
function getValue(name, age) {
console.log(name);
console.log(age);
console.log(this.value);
}
getValue().call(a, 'zyx','24');
getValue().apply(a, ['zyx','24']);
复制代码
不传入第一个参数,那么默认为window
改变了this指向,让新的对象能够执行该函数。那么能够认为是为改新的对象添加一个函数,执行完之后再删除
Function.prototype.myCall = function(context = window, ...rest) {//context就是传入的第一个参数a
context.fn = this; // this就是当前调用call的函数——即getValue;给a添加一个函数:a.fn = getValue
let result = context.fn(...rest); //getValue(...rest)
//将this指向销毁
delete context.fn;
return result;
};
复制代码
Function.prototype.myCall = function(context = window, params = []) {
context.fn = this; //此处this是指调用myCall的function
let result
if (params.length) {
result = context.fn(...params)
}else {
result = context.fn()
}
//将this指向销毁
delete context.fn;
return result;
};
复制代码
var f = function(y, z) {
return this.x + y + z;
}
var a = {x:1}
var g = f.bind(a, 2);
g(3); // => 6 this.x绑定到1,y绑定到2,z绑定到3
复制代码
Function.prototype.myBind = function(context) {//context是a,
if(typeof this !== 'function') { // this就是f
throw new TypeError('Error');
}
var _this = this;
var args = [...arguments].slice(1); //arguments对象是全部(非箭头)函数中均可用的局部变量。此时arguments是 (a,2), [a,2].slice(1) => 获得对象后面的参数args=[2]
return function F() { //由于返回了一个函数,咱们能够new F() 因此要判断
if (this instanceof F) { F就是g,若是f是g的实例 g也存在a
return new _this(...args, ...arguments); //args=[2];arguments是F即g的参数[3];=>new f(2,3)
}
return _this.apply(context, args.concat(...arguments));//若是f不是g的实例,f.apply(a,[2].concat(3))=>f.apply(a,[2,3])
}
}
复制代码
每一个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针
JavaScript中全部的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有本身的原型对象,这样层层上溯,就造成了一个相似链表的结构,这就是原型链。
全部原型链的终点都是Object函数的prototype属性。Objec.prototype指向的原型对象一样拥有原型,不过它的原型是null,而null则没有原型
每一个函数都有prototype属性,除了Function.prototype.bind(),该属性指向原型。
每一个对象都有__proto__属性,实例的__proto__指向构造函数的 prototype。
js 引擎会沿着__proto__-> ptototype 的顺序一直往上方查找,找到 Object.prototype 时,Object.prototype.__proto__又会指向 Object.ptototype,为了不循环引用,原型链没有终点,js 把 Object.prototype.__proto__设置为 null,这样原型链就有了终点。其实原型链查找到 Object.ptototype 这里就中止了查找,若是没有找到,就会报错或者返回 undefined。
最简单的继承实现方式,可是也有其缺点
function Animal() {}
Animal.prototype.name = 'cat'
Animal.prototype.age = 1
Animal.prototype.say = function() {console.log('hello')}
var cat = new Animal()
cat.name // cat
cat.age // 1
cat.say() // hello
复制代码
使用call或apply方法,将父对象的构造函数绑定在子对象上.
function Animal() {
this.species = "动物"
}
function Cat(name, age) {
Animal.call(this)
this.name = name
this.age = age
}
var cat = new Cat('豆豆', 2)
cat.name // 豆豆
cat.age // 2
cat.species // 动物
复制代码
ES5实现继承:组合继承,寄生组合继承
缺点:调用两次父类,形成性能浪费
function Parent(name) {
this.name = name;
}
Parent.prototype.say = function() {
console.log(this.name);
};
function Child(name) {
Parent.call(this, name)
}
Child.prototype = new Parent;//若是没有这一行,Child.prototype.constructor是指向Child的;加了这一行之后,Child.prototype.constructor指向Parent。这显然会致使继承链的紊乱(c明明是用构造函数Child生成的),所以咱们必须手动纠正,将Child.prototype对象的constructor值改成Child
Child.prototype.constructor = Child
let c = new Child("YaoChangTuiQueDuan");
c.say()
复制代码
利用call继承父类上的属性,用一个干净的函数的原型=父类原型,再用子类的原型=这个干净函数的原型
function Parent(name) {
this.name = name;
}
Parent.prototype.say = function() {
console.log(this.name);
};
function ExtendMiddle() {}
function Child(name) {
Parent.call(this, name)
}
ExtendMiddle.prototype = Parent.prototype;
Child.prototype = new ExtendMiddle
let c = new Child("YaoChangTuiQueDuan");
c.say()
复制代码
用 extends 实现继承,必须添加 super 关键字定义子类的 constructor,这里的super() 就至关于 Animal.prototype.constructor.call(this)
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
复制代码
上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
子类必须在constructor方法中调用super方法,不然新建实例时会报错。这是由于子类本身的this对象,必须先经过父类的构造函数完成塑造,获得与父类一样的实例属性和方法,而后再对其进行加工,加上子类本身的实例属性和方法。若是不调用super方法,子类就得不到this对象。
1.建立一个空对象
2.this变量引用该变量
3.继承函数的原型,属性和方法被加入到this引用的对象中
4.新建立的对象由this引用,而且最后隐式的返回this
对于实例对象来讲,都是经过new产生的,不管是function Foo()仍是let a = {b:1}
对于建立一个对象来讲,更推荐使用字面量的方式建立对象(不管性能上仍是可读性上)。若是使用 new Object()的方式建立对象须要经过做用域链一层层找到Object,可是使用字面量就没这个问题。
new的运算符优先级
function Foo() {
return this;
}
Foo.getName = function() {
console.log('1');
}
Foo.prototype.getName = function() {
console.log('2');
}
复制代码
let a = {
age: 1
}
let b = a;
a.age = 2;
console.log(b.age) //2
复制代码
若是给一个变量赋值,二者是同一个引用,其中一方改变,另外一方也会相应改变。 当对象里面的值是简单数据类型,即不是对象类型的时候,浅拷贝就能解决该问题。
let a = {
age: 1
}
let b = Object.assign({}, a);
a.age = 2;
console.log(b.age)// 1
复制代码
let a = {
age: 1
}
b = {...a};
a.age = 2;
console.log(b.age);//1
复制代码
当存在这种状况,咱们要使用深拷贝
let a = {
age: 1,
job: {
first: 'FE'
}
}
let b = {...a};
a.job.first = 'native';
console.log(b.job.first)//native
复制代码
let a = {
age: 1,
job: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.job.first = 'native';
console.log(b.job.first)//FE
复制代码
可是该方法也存在必定的局限性:
可是在一般状况下,复杂数据都是能够序列化的,因此这个方法能够解决大部分问题,而且该函数是内置函数中处理深拷贝最快的。
若是存在上述三种状况能够使用loadsh的深拷贝函数
若是须要拷贝的对象不包含函数,但存在undefined和循环引用的对象,能够使用MessageChannel
function structrualClone(obj) {
return new Promise(resolve => {
cost {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
post1.postMessage(obj);
})
}
var obj = {
a: 1,
b: {
c: b
}
}
//注意该方法是异步的
//能够处理undefined和循环对象
const clone = await structrualClone(obj);
复制代码
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。
Promise 对象表明一个异步操做,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。
特色:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操做成功 */){
resolve(value);
} else {
reject(error);
}
})
复制代码
Promise实例生成之后,能够用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(function(value) {
// success
}, function(error) {
// failure
})
复制代码
then 方法返回的是一个新的Promise实例
Promise.prototype.catch 用于指定发生错误时的回调函数,具备“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误老是会被下一个catch语句捕获
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
复制代码
catch 方法返回的仍是一个 Promise 对象,所以后面还能够接着调用 then 方法
Generator 函数是一个普通函数,可是有两个特征。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
复制代码
调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,一个遍历器对象(Iterator Object)。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法能够恢复执行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
复制代码
async 函数返回一个 Promise 对象,能够使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操做完成,再接着执行函数体内后面的语句
async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数
async 函数返回的 Promise 对象,必须等到内部全部 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误
async 函数内部抛出错误,会致使返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
复制代码
await 命令: await 命令后面是一个 Promise 对象,返回该对象的结果。若是不是 Promise 对象,就直接返回对应的值
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
复制代码
await 命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象.也就是说就算一个对象不是Promise对象,可是只要它有then这个方法, await 也会将它等同于Promise对象
使用注意点:
首选明确两点:
JavaScript 是单线程语言
JavaScript 的 Event Loop 是 JS 的执行机制, 也就是事件循环
执行时,将任务分为两类:
macro-task(宏任务):包括总体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
setTimeout(function(){console.log(1);},0);
new Promise(function(resolve){
console.log(2);
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log(3)
});
console.log(4);
// 2 4 3 1
复制代码
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function(){
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve){
console.log('promise1');
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end');
复制代码
输出:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
export 与 export default 都可用于导出常量、函数、文件、模块等
在一个文件或模块中,export、import 能够有多个,export default 仅有一个
经过 export 方式导出,在导入时要加 { },export default 则不须要
使用 export default命令,为模块指定默认输出,这样就不须要知道所要加载模块的变量名; export 加载的时候须要知道加载模块的变量名
export default 命令的本质是将后面的值,赋给 default 变量,因此能够直接将一个值写在 export default 以后
内存泄漏:是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束
可能形成内存泄漏的操做:
同步模式,又称阻塞模式。javascript 在默认状况下是会阻塞加载的。当前面的 javascript 请求没有处理和执行完时,会阻止浏览器的后续处理
异步加载又叫非阻塞,浏览器在下载执行 js 同时,还会继续进行后续页面的处理
如何实现异步加载 JavaScript
defer属性和async都是属于 script 标签上面的属性,二者都能实现 JavaScript 的异步加载,不一样之处在于:
参考另外一篇总结:juejin.im/post/5caed2…
各排序算法实现:mp.weixin.qq.com/s/gR0kCPRgQ…
一、浏览器的地址栏输入URL并按下回车。
二、浏览器查找当前URL是否存在缓存,并比较缓存是否过时。
三、DNS解析URL对应的IP。
四、根据IP创建TCP链接(三次握手)。
五、HTTP发起请求。
六、服务器处理请求,浏览器接收HTTP响应。
七、渲染页面,构建DOM树。
八、关闭TCP链接(四次挥手)
咱们常见的RUL是这样的:www.baidu.com,这个域名由三部分组成:协议名、域名、端口号,这里端口是默认因此隐藏。除此以外URL还会包含一些路径、查询和其余片断,例如:www.tuicool.com/search?kw=�… 咱们最多见的的协议是HTTP协议,除此以外还有加密的HTTPS协议、FTP协议、FILe协议等等。URL的中间部分为域名或者是IP,以后就是端口号了。一般端口号不常见是由于大部分的都是使用默认端口,如HTTP默认端口80,HTTPS默认端口443。
HTTP缓存有多种规则,根据是否须要从新向服务器发起请求来分类,分为强制缓存,对比缓存。
强制缓存判断HTTP首部字段:cache-control,Expires。
Expires: 是一个绝对时间,即服务器时间。浏览器检查当前时间,若是还没到失效时间就直接使用缓存文件。可是该方法存在一个问题:服务器时间与客户端时间可能不一致。所以该字段已经不多使用。
cache-control: cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。
若是同时存在cache-control和Expires,浏览器老是优先使用cache-control。
对比缓存经过HTTP的last-modified,Etag字段进行判断。
last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,若是不一致则认为缓存已过时并返回新资源给浏览器;若是时间一致则发送304状态码,让浏览器继续使用缓存。
Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,若是变化则返回新资源,不然返回304。
咱们知道在地址栏输入的域名并非最后资源所在的真实位置,域名只是与IP地址的一个映射。网络服务器的IP地址那么多,咱们不可能去记一串串的数字,所以域名就产生了,域名解析的过程实际是将域名还原为IP地址的过程。
在经过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始创建一次链接,这是由TCP协议完成的,主要经过三次握手进行链接。
第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算链接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择本身 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。
发送完毕后,服务器端进入 SYN_RCVD 状态。
第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,而且把服务器发来 ACK 的序号字段+1,放在肯定字段中发送给对方,而且在数据段放写ISN的+1。
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。
完整的HTTP请求包含请求起始行、请求头部、请求主体三部分。
服务器在收到浏览器发送的HTTP请求以后,会将收到的HTTP报文封装成HTTP的Request对象,并经过不一样的Web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。
若是说响应的内容是HTML文档的话,就须要浏览器进行解析渲染呈现给用户。
整个过程涉及两个方面:解析和渲染。
在渲染页面以前,须要构建DOM树和CSSOM树。
在浏览器还没接收到完整的 HTML文件时,它就开始渲染页面了,在遇到外部链入的脚本标签或样式标签或图片时,会再次发送 HTTP 请求重复上述的步骤。在收到CSS文件后会对已经渲染的页面从新渲染,加入它们应有的样式,图片文件加载完马上显示在相应位置。在这一过程当中可能会触发页面的重绘或重排。这里就涉及了两个重要概念:Reflow和Repaint
经过四次挥手关闭链接(FIN ACK, ACK, FIN ACK, ACK)。
第一次挥手(FIN=1,seq=x)
假设客户端想要关闭链接,客户端发送一个 FIN 标志位置为1的包,表示本身已经没有数据能够发送了,可是仍然能够接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,代表本身接受到了客户端关闭链接的请求,但尚未准备好关闭链接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包以后,进入 FIN_WAIT_2 状态,等待服务器端关闭链接。
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭链接时,向客户端发送结束链接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包以后,关闭链接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)以后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭链接,因而本身也关闭链接,进入 CLOSED 状态。
100 Continue 继续,通常在发送post请求时,已发送了http header以后服务端将返回此信息,表示确认,以后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功而且服务器建立了新的资源
202 Accepted 服务器已接受请求,但还没有处理
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 See Other 临时性重定向,且老是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 Bad Request 服务器没法理解请求的格式,客户端不该当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未受权。
403 Forbidden 禁止访问。
404 Not Found 找不到如何与 URI 相匹配的资源。
408 (请求超时) 服务器等候请求时发生超时
500 Internal Server Error 最多见的服务器端错误。
501 Internal Server Error 服务器遇到一个错误,使其没法对请求提供服务
502 (错误网关) 服务器做为网关或代理,从上游服务器收到无效响应
503 Service Unavailable 服务器端暂时没法处理请求(多是过载或维护)。
因为浏览器的 同源策略,在出现 域名、端口、协议有一种不一致时,就会出现跨域,属于浏览器的一种安全限制。
解决跨域问题有不少种方式,经常使用的就是如下几种:
动态建立script,再请求一个带参网址实现跨域通讯.缺点就是只能实现 get 一种请求
两个页面都经过js强制设置document.domain为基础主域,就实现了同域.可是仅限主域相同,子域不一样的跨域应用场景
只服务端设置Access-Control-Allow-Origin便可,前端无须设置,若要带cookie请求:先后端都须要设置
详细参考:www.ruanyifeng.com/blog/2016/0…
同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不须要同源策略,也就不存在跨越问题
就是经过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
总的来讲有如下几点
永远不要信任用户的输入,要对用户的输入进行校验,能够经过正则表达式,或限制长度,对单引号和双"-"进行转换等 永远不要使用动态拼装SQL,能够使用参数化的SQL或者直接使用存储过程进行数据查询存取 永远不要使用管理员权限的数据库链接,为每一个应用使用单独的权限有限的数据库链接 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息
XSS 全称“跨站脚本”,是注入攻击的一种。其特色是不对服务器端形成任何伤害,而是经过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端若是没有过滤或转义掉这些脚本,做为内容发布到了页面上,其余用户访问这个页面的时候就会运行这些脚本。
运行预期以外的脚本带来的后果有不少中,可能只是简单的恶做剧——一个关不掉的窗口:
while (true) {
alert("你关不掉我~");
}
复制代码
理论上,全部可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,漏洞的危害取决于攻击代码的威力,攻击代码也不局限于 script。防护 XSS 攻击最简单直接的方法,就是过滤用户的输入。
若是不须要用户输入 HTML,能够直接对用户的输入进行 HTML escape 。下面一小段脚本:
<script>window.location.href=”http://www.baidu.com”;</script>
复制代码
通过 escape 以后就成了:
<script>window.location.href="http://www.baidu.com"</script>
复制代码
它如今会像普通文本同样显示出来,变得无毒无害,不能执行了。
当咱们须要用户输入 HTML 的时候,须要对用户输入的内容作更加当心细致的处理。仅仅粗暴地去掉 script 标签是没有用的,任何一个合法 HTML 标签均可以添加 onclick 一类的事件属性来执行 JavaScript。更好的方法多是,将用户的输入使用 HTML 解析库进行解析,获取其中的数据。而后根据用户原有的标签属性,从新构建 HTML 元素树。构建的过程当中,全部的标签、属性都只从白名单中拿取。
CSRF(XSRF) 顾名思义,是伪造请求,冒充用户在站内的正常操做。
例如,一论坛网站的发贴是经过 GET 请求访问,点击发贴以后 JS 把发贴内容拼接成目标 URL 并访问:
http://example.com/bbs/create_post.php?title=标题&content=内容
复制代码
那么,咱们只须要在论坛中发一帖,包含一连接:
http://example.com/bbs/create_post.php?title=我是脑残&content=哈哈
复制代码
只要有用户点击了这个连接,那么他们的账户就会在不知情的状况下发布了这一帖子。可能这只是个恶做剧,可是既然发贴的请求能够伪造,那么删帖、转账、改密码、发邮件全均可以伪造。
关键操做只接受 POST 请求
验证码
CSRF 攻击的过程,每每是在用户不知情的状况下构造网络请求。因此若是使用验证码,那么每次操做都须要用户进行互动,从而简单有效的防护了CSRF攻击。
可是若是你在一个网站做出任何举动都要输入验证码会严重影响用户体验,因此验证码通常只出如今特殊操做里面,或者在注册时候使用。
检测 Referer
常见的互联网页面与页面之间是存在联系的,好比你在 www.baidu.com 应该是找不到通往www.google.com 的连接的,再好比你在论坛留言,那么无论你留言后重定向到哪里去了,以前的那个网址必定会包含留言的输入框,这个以前的网址就会保留在新页面头文件的 Referer 中
经过检查 Referer 的值,咱们就能够判断这个请求是合法的仍是非法的,可是问题出在服务器不是任什么时候候都能接受到 Referer 的值,因此 Referer Check 通常用于监控 CSRF 攻击的发生,而不用来抵御攻击。
Token
目前主流的作法是使用 Token 抵御 CSRF 攻击。下面经过分析 CSRF 攻击来理解为何 Token 可以有效
CSRF 攻击要成功的条件在于攻击者可以预测全部的参数从而构造出合法的请求。因此根据不可预测性原则,咱们能够对参数进行加密从而防止 CSRF 攻击。
另外一个更通用的作法是保持原有参数不变,另外添加一个参数 Token,其值是随机的。这样攻击者由于不知道 Token 而没法构造出合法的请求进行攻击。
【Token 使用原则】
注意:过滤用户输入的内容不能阻挡 csrf,咱们须要作的是过滤请求的来源。
XSS(跨站脚本攻击——Cascading Style Sheets,为不和层叠样式表 缩写混淆,故将跨站脚本攻击缩写为XSS )是获取信息,不须要提早知道其余用户页面的代码和数据包。
CSRF(跨站请求伪造——Cross-site request forgery)是代替用户完成指定的动做,须要知道其余用户页面的代码和数据包。要完成一次CSRF攻击,受害者必须依次完成两个步骤 登陆受信任网站A,并在本地生成Cookie 在不登出A的状况下,访问危险网站B
是互联网上应用最为普遍的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它能够使浏览器更加高效,使网络传输减小.HTTP 协议构建于 TCP/IP 协议之上,是一个应用层协议,默认端口号是 80
简单快速:客户向服务器请求服务时,只需传送请求方法和路径
灵活:HTTP容许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记
无状态:HTTP协议是无状态协议( Cookie 的出现)
无链接:无链接的含义是限制每次链接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开链接 (深刻-持久链接、管线化)
若是客户端浏览器支持 Keep-Alive ,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有 Connection:Keep-Alive的请求时,它也会在响应头中添加一个一样的字段来使用 Keep-Alive 。这样一来,客户端和服务器之间的HTTP链接就会被保持,不会断开(超过 Keep-Alive 规定的时间,意外断电等状况除外),当客户端发送另一个请求时,就使用这条已经创建的链接。
在 HTTP 1.1 版本中,默认状况下全部链接都被保持,若是加入 "Connection: close" 才关闭。目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起 Keep-Alive 的链接请求了,因此是否能完成一个完整的 Keep-Alive 链接就看服务器设置状况。
HTTP Keep-Alive 简单说就是保持当前的TCP链接,避免了从新创建链接。
HTTP 长链接不可能一直保持,例如 Keep-Alive: timeout=5, max=100,表示这个TCP通道能够保持5秒,max=100,表示这个长链接最多接收100次请求就断开。
HTTP 是一个无状态协议,这意味着每一个请求都是独立的,Keep-Alive没能改变这个结果。另外,Keep-Alive也不能保证客户端和服务器之间的链接必定是活跃的,在HTTP1.1版本中也如此。惟一能保证的就是当链接被关闭时你能获得一个通知,因此不该该让程序依赖于 Keep-Alive 的保持链接特性,不然会有意想不到的后果。
使用长链接以后,客户端、服务端怎么知道本次传输结束呢?两部分:1. 判断传输数据是否达到了Content-Length 指示的大小;2. 动态生成的文件没有 Content-Length ,它是分块传输(chunked),这时候就要根据 chunked 编码来判断,chunked 编码的数据在最后有一个空 chunked 块,代表本次传输数据结束。
是以安全为目标的HTTP通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层(Secure Sockets Layer),HTTPS 的安全基础是 SSL ,所以加密的详细内容就须要 SSL
HTTPS加密、加密、及验证过程,以下图所示:
https协议须要到ca申请证书,通常免费证书较少,于是须要必定费用
http 是超文本传输协议,信息是明文传输,https 则是具备安全性的 ssl 加密传输协议
http 和 https 使用的是彻底不一样的链接方式,用的端口也不同,前者是 80 ,后者是 443
http 的链接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全
HTTP使用TCP三次握手创建链接,客户端和服务器须要交换3个包(可参考 HTTP服务的七层架构技术解析及运用 user-gold-cdn.xitu.io/2019/4/17/1…)
这个没什么好说的,就是用户在浏览器里输入一个https网址,而后链接到server的443端口。
二、服务端的配置
采用HTTPS协议的服务器必需要有一套数字证书,能够本身制做,也能够向组织申请,区别就是本身颁发的证书须要客户端验证经过,才能够继续访问,而使用受信任的公司申请的证书则不会弹出提示页面(startssl就是个不错的选择,有1年的免费服务)。
这套证书其实就是一对公钥和私钥,若是对公钥和私钥不太理解,能够想象成一把钥匙和一个锁头,只是全世界只有你一我的有这把钥匙,你能够把锁头给别人,别人能够用这个锁把重要的东西锁起来,而后发给你,由于只有你一我的有这把钥匙,因此只有你才能看到被这把锁锁起来的东西。
三、传送证书
这个证书其实就是公钥,只是包含了不少信息,如证书的颁发机构,过时时间等等。
四、客户端解析证书
这部分工做是有客户端的TLS来完成的,首先会验证公钥是否有效,好比颁发机构,过时时间等等,若是发现异常,则会弹出一个警告框,提示证书存在问题。
若是证书没有问题,那么就生成一个随机值,而后用证书对该随机值进行加密,就好像上面说的,把随机值用锁头锁起来,这样除非有钥匙,否则看不到被锁住的内容。
五、传送加密信息
这部分传送的是用证书加密后的随机值,目的就是让服务端获得这个随机值,之后客户端和服务端的通讯就能够经过这个随机值来进行加密解密了。
六、服务段解密信息
服务端用私钥解密后,获得了客户端传过来的随机值(私钥),而后把内容经过该值进行对称加密,所谓对称加密就是,将信息和私钥经过某种算法混合在一块儿,这样除非知道私钥,否则没法获取内容,而正好客户端和服务端都知道这个私钥,因此只要加密算法够彪悍,私钥够复杂,数据就够安全。
七、传输加密后的信息
这部分信息是服务段用私钥加密后的信息,能够在客户端被还原。
八、客户端解密信息
客户端用以前生成的私钥解密服务段传过来的信息,因而获取了解密后的内容,整个过程第三方即便监听到了数据,也一筹莫展。
TCP/IP模型分为四层:
在TCP/IP模型中并不包含物理层。另外,两个重要的协议ARP(Address Resolution Protocol,地址解析协议)和RARP(Reverse Address Resolution Protocol,反向地址转换协议),在OSI模型中通常被认为是在位于第二层数据链路层和第三层网络层之间,而在TCP/IP模型中则位于网络接口层。
详细描述参考: juejin.im/post/5a98e1…
参考另外一篇总结:juejin.im/post/5cb822…