知识积累
https://juejin.im/post/5e9518b3518825738e21794c#heading-2
map与forEach的区别:
1、map有返回值,可以return出来;
arr[].map(function(value,index,array){ xxx return xxx });
-
参数:value数组中的当前项,index当前项的索引,array原始数组
-
区别:map的回调函数中支持return返回值,return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆了一份,把克隆这一份的数组中的对应项改变了 );
var array = [10,34,57,43,76]; var res = array.map(function (item,index,input) { return item*10; }) console.log(res); console.log(array);不变
2、关于forEach()
没有返回值!!!!
arr[].forEach(function(value,index,array){
xxxxx
})
-
参数:value数组中的当前项,index当前项的索引,array原始数组;
-
数组中有几项,那么传递进去的匿名回调函数就需要执行几次
-
理论上这个方式是没有返回值的,只是遍历数组中的每一项,不对原来数组进行修改,但是可以自己通过数组的索引来修改原来的数组
var array = [10,34,57,43,76]; var res = array.forEach(function (item,index,input) { input[index] = item*10; }) console.log(res);//--> undefined; console.log(array);//--> 通过数组索引改变了原数组; [100,340,570,430,760]
安全性
-
跨站脚本攻击(
Cross-site scripting
,简称XSS
)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了
HTML
以及用户端脚本语言。 -
跨站请求伪造(
Cross-site request forgery
,简称XSRF
)是一种对网站的恶意利用。与
XSS
不同,XSS
利用站点内的信任用户,而CSRF
则通过伪装来自受信任用户的请求来利用受信任的网站。CSRF
比XSS
更具危险性。
预防措施:有 Token验证、Referer验证、隐藏令牌 三种。
一、用token来防范csrf攻击要如何操作
我的回答:
因为csrf攻击无法获取表单中的内容,将token放在post请求的表单里,服务端会根据表单里的token来验证用户是否是真实用户。
权威资料:
CSRF(Cross-site request forgery)跨站请求伪造
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
防范策略:
-
同源检测。
根据请求头的Origin和Referer判断是否是信任的域名发起的请求,若不是则阻止,若无这两个个请求头参数,则直接阻止
-
CSRF Token
原理:让请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。
具体操作:用户登录时根据用户名和密码等加密生成一个Token值,同时写入到浏览器的Cookie和Hidden中,然后根据每次的请求将Token和Cookie同时带入到服务端进行验证和判断。
二、如何给ul下的li标签绑定事件
用事件委托,给外层的ul绑定事件,根据事件冒泡的原理内层的li也被绑定,好处是如果再多了几个li标签,就不用重复绑定了,而且比遍历绑定更省资源
还可以通过e.target.nodeName
来指定某个标签才响应该事件
window.onload = function(){ var oUl = document.getElementById("ul1"); oUl.onclick = function(ev){ var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == 'li'){ alert(123); alert(target.innerHTML); } } }
三、前端实时更新
- 用meta标签refresh,设置前端多长时间再向服务端更新
setInternal(func, 1000)
- 在ajax成功请求后用
setTimeout()
递归调用ajax请求 - web Socket
四、有没有对网站的性能分析过,比如performance工具
window.performance.timing保存着各种时间
补充说了前端性能方面:白屏时间、首屏时间
更多详细内容可以看本人的另一篇博客:前端性能监控方案(首屏、白屏时间等)
五、JS基础
Javascript基础
变量和类型
1.变量
js
的变量,说白了就相当于是一个存储数据的‘容器’。我们在定义变量的时候需要注意一下几点:
- 变量名大小对大小写字母敏感;
- 变量名须以字母开头(也能以
$
和 _ 符号开头,但是不建议);
2.类型
js规定的数据类型有八种:Boolean, String, Number, Null, Undefined, bigInt, Symbol, Object
基本类型有六种: Boolean, String, Number, Null, Undefined,Symbol
引用数据类型有一种: Object(在JS中除了基本数据类型以外的都是对象,Data, function, Array,正则表达式都是对象)
注意:Symbol 是 ES6 引入的一种新的原始数据类型,表示独一无二的值。
拓展: 关于null
和undefined
的区别和相似之处。于null
和undefined
在一定程度上及其相似,但是又有着细微的区别。
null
表示的是‘没有对象’,即该处不应该有值。(也可以这样理解,null是已经定义但是值为空,分配内存);
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的终点。
undefined
表示“未定义”,一个特殊值,通常用于指示变量尚未赋值,其值就为undefined
。
- 变量被声明了,但没有赋值时,就等于
undefined
(较为常见)。 - 调用函数时,应该提供的参数没有提供,该参数等于
undefined
。 - 对象没有赋值的属性,该属性的值为
undefined
。 - 函数没有返回值时,默认返回
undefined
。
原型和原型链
说到原型和原型链,不免得说一下构造函数。
构造函数是一种特殊的方法。主要用来在创建对象时初始化对象。每个构造函数都有prototype
(原型)属性。构造函数的出现是为了解决使用Object
构造函数和字面量表示法不方便创建大量重复对象的问题。看个例子:
function func(name){ this.name=name; this.sayHello=function(){ alert('my name is '+this.name); } } var f=new func('phoebe'); f.sayHello(); 复制代码
此处的f
是func
实例化之后的一个构造函数,new
是用来实例化函数的一种手段。而func
的构造函数实际上是js
内置的function
,实际上function func(name){}
等价于var func = function (name){}
。到这里相信你大概已经明白了何为构造函数。
知晓了构造函数的概念之后,我们来探讨一下原型和原型链。
1.原型
prototype
每个构造函数在创建时都会自动创建一个prototype属性,我们可以在这个属性当中添加新的属性和方法,添加的属性和方法可以为这个函数所使用。将这个函数实例化后的实例也可以使用新添加的属性和方法。这个prototype属性指向的对象,包括自带的属性和我们添加的属性、方法,可以把它指向的对象的内容集合称为构造函数的原型。
注意prototype是构造函数才有的属性。
__ proto __
__proto__
是每个对象自带的属性,属性值是当前实例所属类的原型(prototype
),原型对象中有一个属性constructor
, 它指向函数对象。
举了例子:
function Example() {} var ExampleOne = new Example() console.log(ExampleOne.__proto__ === Example.prototype) // true console.log(Example.prototype.constructor===Example) // true // ExampleOne是个实例对象,带有的是__proto__属性,Example是个构造函数,带的是prototype属性 复制代码
图解:
constructor
每个函数都会有一个原型对象,该原型对象有一个constructor
属性,会指向创建对象的函数本身。此外,所有的实例对象都可以访问onstructor
属性,constructor
属性是创建实例对象的函数的引用。
2.原型链
一般,每个对象都会有一个原型__proto__
,这个原型也有它自己的原型,将这些原型连接起来,形成一个原型链。在查找某一特定属性时,会先去这个对象里去找,如果对象上没有的话就会去它的原型对象里面去,单还是没有的话,会再去向原型对象的原型对象里去寻找。 这个寻找的操作被委托在整个原型链上,我们称之为原型链。
图解:
举个例子:
function Example(){} var ExampleOne = new Example(); //new实例化 console.log(ExampleOne.__proto__ === Example.prototype); // true console.log(Example.prototype.__proto__ === Object.prototype) //true console.log(Object.prototype.__proto__) //null Example.__proto__ == Function.prototype; //true console.log(Function.prototype)// function(){} (这是个空函数) var number = new Array() console.log(number.__proto__ == Array.prototype) // true console.log( Array.prototype.__proto__ == Object.prototype) // true console.log(Array.prototype) // [] (这是个空数组) console.log(Object.prototype.__proto__) //null console.log(Array.__proto__ == Function.prototype)// true 复制代码
小结:
__proto__
是对象的属性,prototype
是构造函数的属性,__proto__
总指向prototype
;prototype
在构造函数创建时会自动生成,它总会被__proto__
指向。
作用域和闭包
1.作用域
我们先来说一下变量的作用域。变量的作用域一般分为两种:全局作用域和局部作用域。
全局作用域:函数最外层定义的变量,任何函数内部都可以访问到。
例如
var a = '1'; function change() { console.log(a) }; change() // 1 复制代码
局部作用域: 和全局作用域不同,局部作用域只能允许自身内部调用变量,外部函数无法访问。
例如:
function num() { var b='2' }; num(); console.log(b) // b is not defined 复制代码
需要注意的是,在函数内部声明一个变量的时候,一定要记住得用var
定义,不然相当于声明了一个全局变量。
例如:
function change() { num=2; } change(); console.log(num) // 2 复制代码
需要注意的是,函数内部存在的变量提升问题。
我们先看下面例子:
var num1 = 1; function one() { console.log(num1); // undefined var num1 = 2; console.log(num1) // 2 } 复制代码
其实上面的例子等价于:
var num1 = 1; function one() { var num1 console.log(num1); // undefined num1 = 2; console.log(num1) // 2 } 复制代码
不难看出,这是存在在函数内部变量提升的现象。
或许对概念不清晰的童鞋对于第一个例子会有点疑惑,认为第一个打印出来的应该是1,而不是undefined
(寄拖鞋警告一次)。
为什么呢?其实one()
函数内部声明了num1
,one()
此时就是一个局部作用域,在内部没有声明num1
的情况下,是会直接获取全局变量num1
。
但是在局部作用域声明了num1
之后,num1
这个变量会提升。如第一个例子,当第一次console.log(num1)
的时候,就相当于var num1
,定义了一个变量但没有赋值,第二次打印,会打印出赋值之后的num1
,就像上面的第二个例子。
拓展:在这里,想拓展一下几种常见的声明变量的方式。
var
:如果在当前函数内部声明一个变量,则作用范围在函数内部;如果在最外层声明,则作为全局变量;如果未使用var
定义直接使用变量,则会报错;const
:具有块级作用域的特征,同一个作用域中,变量名只能声明一次,不存在变量提升。const
声明的变量必须是个常量。let
: 跟const
几乎类似,但是最主要的区别是let
声明的是一个变量,const
声明的必须是个常量。
不同之处:
*var
存在变量提升,let
和const
不会;
var
在函数内部同一个变量可以重复声明,而在同一个块级作用域内部,let
和const
只能声明一次,并且const
声明的是个常量,不能修改;var
声明的变量属于函数作用域,let
和const
声明的变量属于块级作用域
2.闭包
简单的说,闭包有两个作用:一就是能够读取其他函数内部变量的函数(也就是读取自身函数以外的变量)。二是让这些外部变量始终保存在内存中。
闭包可以避免使用全局变量,防止变量污染,但是过多使用会造成内存泄露。
举个例子,我想要获取一个函数内部的变量:
var num = 200; function f1() { var num = 100; return a }; f1() // 100 var fn = f1(); fn() // fn is not a function 复制代码
这显然是不行的,fn
获取不到f1
内部的变量。这时候我们可以考虑在f1
内部返回一个函数看看结果:
var num = 200; function f1() { var num = 100; return function () { return num } }; var fn = f1(); fn() // 100 复制代码
通过在函数内部返回一个函数,外部函数可以根据返回的函数获取到原来函数内部的变量,这就体现了闭包的作用。
想研究的更透彻欢迎猛戳:
developer.mozilla.org/zh-CN/docs/…
执行机制
javascipt
是单线程的描述性脚本语言,与java
或C#
等编译性语言不同,它不需要进行编译成中间语言,而是由浏览器进行动态地解析与执行。所以,弄懂它的执行机制是很有必要的。
由于javascript
是单线程的,为了防止在网页加载过程中由于图片音乐等过大文件而导致加载阻塞,从而衍生出了‘同步任务’和‘异步任务’。我们可以先看一下如下流程图:
通过上图可以较为清晰的看到任务执行的流程。
在同步任务和异步任务之外,执行任务的时候还定义了‘宏观任务’和‘微观任务’两种。一般来说:
macro-task(宏任务):包括整体代码script,setTimeout,setInterval;
micro-task(微任务):Promise,process.nextTick;
任务一开始执行的时候,会进入到相应的Event Queue
当中。事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。
虽然说js
是单线程的,但是并不是简单意义上的就是按顺序往下执行。通过以上所讲的这些执行顺序,相信你们应该在心里有个大概的思路了(拖鞋警告二)。看个例子(网上搜的,就得解释的可以就拿过来了):
console.log('1'); setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }) process.nextTick(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) 复制代码
最终输出的顺序是:1,7,6,8,2,4,3,5,9,11,10,12
。
有没有跟你所预想的一样?如果是,那么恭喜这位童鞋,你已经大致掌握了js
的执行机制了。如果不是,那就往下瞅瞅。
第一轮事件循环流程分析如下:
整体script
作为第一个宏任务进入主线程,遇到console.log
,输出1。
遇到setTimeout
,其回调函数被分发到宏任务Event Queue
中。我们暂且记为setTimeout1
。
遇到process.nextTick()
,其回调函数被分发到微任务Event Queue
中。我们记为process1
。
遇到Promise
,new Promise
直接执行,输出7
。then
被分发到微任务Event Queue
中。我们记为then1
。
又遇到了setTimeout
,其回调函数被分发到宏任务Event Queue
中,我们记为setTimeout2
。
宏观任务(Event Queue ) | 微观任务(Event Queue ) |
---|---|
setTimeout1 |
process1 |
setTimeout2 |
then1 |
上表是第一轮事件循环宏任务结束时各Event Queue
的情况,此时已经输出了1和7。
我们发现了process1
和then1
两个微任务。
执行process1
,输出6。
执行then1
,输出8。
第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8
。那么第二轮时间循环从setTimeout1
宏任务开始:
首先输出2。
接下来遇到了process.nextTick()
,同样将其分发到微任务Event Queue
中,记为process2
。new Promise
立即执行输出4,then
也分发到微任务Event Queue
中,记为then2
。
宏观任务(Event Queue ) | 微观任务(Event Queue ) |
---|---|
setTimeout2 |
process2 |
then2 |
第二轮事件循环宏任务结束,我们发现有process2
和then2
两个微任务可以执行。
输出3。
输出5。
第二轮事件循环结束,第二轮输出2,4,3,5
。
第三轮事件循环开始,此时只剩setTimeout2
了,执行。
直接输出9。
将process.nextTick()
分发到微任务Event Queue
中。记为process3
。
直接执行new Promise
,输出11。
将then
分发到微任务Event Queue
中,记为then3
。
宏观任务(Event Queue ) | 微观任务(Event Queue ) |
---|---|
process3 |
|
then3 |
第三轮事件循环宏任务执行结束,执行两个微任务process3
和then3
。
输出10。
输出12。
第三轮事件循环结束,第三轮输出9,11,10,12
。
最终整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12
。
(需要注意的是,node
环境下的事件监听依赖libuv与前端环境不完全相同,输出顺序可能会有误差)
最后补充一点,谨记javascript
是一门单线程语言,而Event Loop
是javascript
的执行机制。
六、CSS
1.常用的几种布局方式
固定布局:最外层盒子宽度固定且不能移动,里面的各个模块也是固定宽度而不是百分比。无论访问者的屏幕的分辨率是多少,网页都显示为和其他访问者相同的宽度。 流式布局(自适应布局):盒子宽高按百分比(故而也称之为百分比布局)。 定位布局: 使用决定定位,相对定位和固定定位的布局 浮动布局:使用float:left;和float:right;设置布局,注意清除浮动。 响应式布局(媒体查询): 使用@media,详情可参考:www.runoob.com/cssref/css3… 弹性布局(伸缩布局):献上阮一峰老师的细致分析文档:www.ruanyifeng.com/blog/2015/0…
2、关于BFC
-
BFC
是什么BFC(Block Formatting Context)
块级格式化上下文,是用于布局块级盒子的一块渲染区域。BFC是web页面CSS视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域。 -
BFC
的作用BFC
是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然。我们可以用BFC来阻止元素被浮动元素覆盖、阻止浏览器因为四舍五入造成的多列布局换行的情况、阻止相邻元素的margin合并等。同时,BFC还可以包含浮动元素。
-
BFC
的约束规则(1)内部的元素会在垂直方向上一个接着一个的放置。计算
BFC
的高度时,需要注意浮动元素也参与计算;(2)
Box
垂直方向的距离由margin
决定。注意:属于同一个BFC
的两个相邻的Box
的margin
会发生重叠;(3)生成
BFC
元素的子元素中,每一个子元素的margin
与包含块的左边界border
相接触(对于从左到右的格式化,否则相反),就算是在浮动中也一样;(4)
BFC
的区域不会与float box
重叠。(5)
BFC
相当于页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。相反也一样。 -
BFC
可以解决哪些问题margin
重叠问题、文本不环绕浮动元素问题、包裹浮动元素没有高度问题(父元素塌陷问题)等。 -
BFC
触发方式(一种即可)(1)
float
的值不为none
;(2)
overflow
的值不为visible
;(3)
display
的值为table-cell
、tabble-caption
和inline-block
之一;(4)
position
的值不为static
或则releative
中的任何一个; -
BFC
布局与普通文档流布局区别普通文档流布局规则
(1)浮动的元素是不会被父级计算高度;
(2)非浮动元素会覆盖浮动元素的位置;
(3)
margin
会传递给父级;(4)两个相邻元素上下
margin
会重叠;BFC
布局规则(1)浮动的元素会被父级计算高度(父级触发了
BFC
);(2)非浮动元素不会覆盖浮动元素位置(非浮动元素触发了
BFC
);(3)
margin
不会传递给父级(父级触发了BFC
);(4)两个相邻元素上下
margin
会重叠(给其中一个元素增加一个父级,然后让他的父级触发BFC
);
作者:hh_phoebe
链接:https://juejin.im/post/5e9518b3518825738e21794c
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。