1. 简述一下对HTML语义化的理解
答:
(1)用正确的标签做正确的事情;
(2)在没有CSS的情况下也能让页面呈现出清晰的结构,容易阅读;
(3)有利于SEO,和搜索引擎建立良好的沟通,有助于爬虫抓取更多的有效信息(爬虫依赖于HTML标记来确定上下文和各个关键字的权重);
(4)便于团队开发和维护,语义化更具有可读性,使开发者对网络更容易将网站分块。
2. new操作符具体干了什么?
(1)创建一个空对象,并且this变量引用该对象,同时还继承该函数的原型;
(2)属性和方法被加入到this引用的对象中;
(3)新创建的对象由this所引用,并且最后隐式的返回this.
function School(){
var name = "Tom";
var fn = function(){}
}
var obj = new School();
obj.name //undefined
obj.fn //undefined
//obj是一个空对象:School {}
// __proto__:
// constructor: ƒ School()
// __proto__: Object
3. 说说你对作用域链的理解
PS: [[scope]],作用域,[[scope]]中存储了运行期上下文的集合;作用域链:[[scope]]中所存的执行期上下文对象的集合,这个集合程链式结构,所以把它叫作用域链。
作用域链的作用是 保证执行环境里有权访问的变量和函数是有序的。作用域链的变量的访问只能向上访问,变量访问到window对象即被终止。作用域链向下访问是不被允许的。
4. HTTP和HTTPS
HTTP协议通常承载于TCP协议之上,在HTPP和TCP之间添加一个安全协议层(SSL或TLS),这个时候就变成了HTTPS。
默认HTTP的端口号为80,HTTPS的端口号为443.
为什么HTTPS更安全?
因为网络请求需要中间有很多的服务器、路由器的转发。中间的节点都有可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有,https它利用ssl/tsl协议传输,它包含证书、卸载、流量转发、负载均衡、页面适配、浏览器适配,refer传输等,保证传输过程的安全性。
5. TCP与UDP
1)TCP(Transmission Control Protocol,传输控制协议),是基于连接的协议,即:在正式收发数据前,必须和对方建立可靠的连接。
一个TCP连接必须要经过三次“对话”才能连接起来。
2) UDP(User Data Protocol,用户数据报协议),是与TCP相对应的协议,它是面向非连接的协议,不与对方建立连接,直接把数据包传送过去。
UDP适用于一次只传少量数据,对可靠性要求不高的应用环境。
6. TCP传输的三次握手和四次挥手策略
为了准确无误地把数据送到目标处,TCP采用三次握手策略。握手过程中使用了TCP的标志:SYN(同步标志)和ACK(确认标志)。SYN=1代表这是一个连接请求,或连接接收报文;握手完成后,SYN标志位置0。ACK=1时,确认号字段才有效。
“三次握手”:
1) 发送端发一个带SYN标志的数据包给对方;
2) 接收端收到后,回传一个带SYN/ACK标志的数据包以示传送确认信息;
3) 最后,发送端再回传一个带ACK标志的数据包,代表“握手结束”。
断开一个TCP连接需要“四次挥手”:
第一次挥手:浏览器发送一个FIN(终止标志),用来关闭浏览器到服务器的数据传送,即告知对方,我不给你发数据了,但此时还可以接收数据;
第二次挥手:服务器收到FIN包后,发一个ACK给对方,确认序号为收到序号+1;
第三次挥手: 服务器方发一个FIN,关闭到浏览器的数据传送(不给你发了);
第四次挥手: 浏览器收到FIN后,发一个ACK给服务器,确认序号为收到序号+1.
为什么连接时三次,而关闭时要四次呢?
因为当Server端收到Client端的请求后,可直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
但当关闭连接时,当Server端收到FIN报文时,很可能不会立即关闭SOCKET,所以只能先回复一个ACK报文,告知Client端“你的FIN已收到”。只有等到我Server端所有报文都发送完了,才能发送FIN报文。因此不能一起发送。
7. 前端存储的方式
1)cookies(在计算机上存储在小的文本文件中的数据,解决连接关闭后记住用户信息)
a. 网站为标识用户身份而储存在用户本地终端上的数据,是经过加密了的;数据在http协议中携带,在浏览器和服务器之间来回传递
b. 它兼容性好,请求头自带cookie方便,缺点是大小只有4k,自动请求头加入cookie浪费流量,每个domain限制20个cookie,使用起来麻烦需要自行封装
c. 设置的cookie在过期之前一直有效,即使窗口或浏览器关闭
d. 它有极高的扩展性和可用性
重要:通过JavaScript创建cookie:JS可用document.cookie创建,读取和删除cookie
a.创建cookie:
document.cookie="username=kbin; expires=Sun, 31 Dec 2017 12:00:00 UTC;path=/"
b. 读取cookie:(同时会以一条字符串的形式返回所有cookie)
var x=document.cookie
c. 改变cookie:
document.cookie="username=kbin2; expires=Sun, 31 Dec 2017 12:00:00 UTC;path=/"
d. 删除cookie:(只需将expires参数的时间设置为过去的时间)
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
2)localStorage
HTML5加入的以键值对(key-value)为标准的方式。
优点是操作方便,永久性储存,除非手动删除,大小为5M,兼容IE8+
localStorage.setItem("key","value") //以"key"为名称存储一个值"value"
localStorage.getItem("key")//获取名称为"key"的值
localStorage.removeItem("key")//删除名称为"key"的信息
localStorage.clear() //清空localstorage中所有的信息
3)sessionStorage
与localStorage基本类似,但它的数据在页面关闭后会被清理,而且与cookie和localStorage不同,它不能在所有同源窗口中共享,是会话级别的储存方式
重要:localStorage和sessionStorage使用相同的API
4)IndexedDB
NoSQL数据库,用键值对进行储存,可进行快速读取操作,非常适合web场景,同时用JavaScript进行操作会非常方便
PS:三者对比一览表:
8. 同源和跨域
1)同源
a. 概念:也叫同源策略,是浏览器的一种安全机制。
b. 规则:同源指‘三个相同’:协议名相同,域名相同,端口号相同(即,同一个网站)
PS: https://www.github.com/80 其中,https是协议名,www.github.com是子域名,github.com是主域名,80是端口号
c. 目的:保护用户信息安全,防止恶意网站窃取信息
2)跨域
一个网站的网址组成包括协议名,子域名,主域名,端口号。当在页面中一个url请求数据时,如果这个url的协议名,子域名,主域名,端口号有任意一个不相同,就会产生跨域问题。(同源策略:只能是同源的网站之间才可以请求数据)
为了实现不同源网站之间可以相互请求数据,所以要解决跨域问题,跨域解决方案:
a. jsonp(json with padding):把JSON填充到一个盒子里
因为浏览器的script, img, iframe标签是不受同源策略限制的,所以通过动态插入script标签引入一个js文件,这个js文件载入成功后会执行url参数中指定的callback函数,并把我们需要的json数据作为参数传入。在服务器端,当req.params参数中带有callback属性时,则把数据作为callback的参数执行,并拼接成一个字符串后返回。
网页端代码:
<script>
//json数据请求数据(ajax发送请求只用于同源网站之间)
function tryJsonp(data){
alert("receive");
alert(data.name); //要从后台拿到数据
}
</script>
//把函数名上传给后台,避免了前后台交互时,函数名不一致导致获取不到数据的问题
<script src="http://www.perguo.com/qy/test?callback=tryJsonp"></script>
服务器端代码(node.js写后端)
router.get("/test", function(req, res) {
var data = { name: "Guo" };
var query = req.query;
if(query && query.callback) {
var str = query.callback + '(' + JSON.stringify(data) + ')';
res.send(str);
} else {
res.send('OK');
}
})
优点:兼容性好,简单易用,支持浏览器与服务器双向通信。
缺点:只支持get请求,且只支持跨域HTTP这种情况(不支持HTTPS)。
b. 直接在服务器端设置跨域资源访问CORS(Cross-Origin Resource Sharing)
设置Request Header头中Access-Control-Allow-Origin为指定可获取数据的域名(一劳永逸)。
思想:在服务器端检查请求头部的origin,从而决定请求应该成功还是失败。Node.js实现CORS方法代码:
app.use(function (req, res, next) {
res.header("Access-Contron-Allow-Origin", "*");//允许所有来源访问
res.header("Access-Control-Allow-Origin", "Origin, X-Requested-With, Content-Type, Accept");
next();
})
app.use('/', routes);
app.use('/users', users);
c. 找‘爸爸’,寻找相同主域document.domain. 通过修改document.domain来跨子域。
例如:http://www.example.com/a.html和http://bbs.example.com/b.html,这俩主域名都是example.com,相当于有同一个爸爸。此方法只适用于两个iframe之间的跨域。
document.domain = 'example.com'.
d. 通过window.name来跨域接收数据
不同域的框架把想要共享的信息放在window.name里,此方法只适用于两个iframe之间的跨域。
name属性有一个特征:在一个窗口(window)的生命周期内,窗口载入的所有页面都是共享一个window.name的,每个页面对window.name都有读写的权限。
window.name是持久存在一个窗口载入过的所有页面中的。
e. 代理方式
如:www.123.com/index.html需调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回index.html。这就是一个代理模式,相当于绕过了浏览器端,自然就不存在跨域问题。
f. php端修改Header方式
在php接口脚本中加入一下两句即可:
header('Access-Control-Allow-Origin', '*'); //允许所有来源访问
header(‘Access-Control-Allow-Method: POST, GET'); //允许访问方式
9. 闭包的作用和不足
1)什么是闭包?
当内部函数被保存到了外部,就会产生闭包。
本质上,闭包是将函数的内部和外部连接起来的桥梁。
用处:(1)读取函数内部变量
(2)这些变量的值始终保存在内存中,不会在外层函数调用后被自动清除。
2)闭包的作用
(1)实现公有变量。不依赖外部变量可反复执行,如:累加器
(2)可做缓存(存储结构)。当多个函数同时与一个函数产生闭包时,它们之间的变量可以共用
(3)可实现封装,属性私有化
(4)模块化开发,防止污染全局变量
3)缺点
闭包会导致原有作用域链不释放,造成内存泄漏。所以,在函数退出之前,将不使用的局部变量全部删除(null)
10. Promise是什么?
promise是异步比编程的一种解决方案,比传统的回调函数和事件更合理和强大。简单来说就是一个容器,里面保存着某个未来才会结束的事情。从语法上来说,它是一个对象。
11. 网络请求编码
1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求
200:交易成功(服务器成功返回数据)
400:错误请求(如语法错误)
403:请求不允许
404:没有发现文件,URL或查询
500:服务器产生内部错误
502:服务器暂时不可用(过载)
12. JavaScript中的隐式转换
1.[]
!![] == true //true
[] == true //false
![] == [] //true
原因:[]是javascript中比较特殊的隐式转换
String([]) //""
Number([]) //0
Boolean([]) //true
[] == true // 0 == 1 ->false
// !是逻辑非,将布尔值求反;!!是类型转换,将对应类型转为Boolean型
![] == [] //![] -> false; false == 0 ->true
!![] == true //true == true ->true
不要晕,我们再来一段:
[] == 0 //true
![] == 0 //true (Boolean([])是true)
[] == "" //true
!![] == "" //false
"" == true //false
2. null 与 undefined
null == undefined //true
3. 一个是数字,一个是字符串 。先将字符串转为数字,再比较
4. 其中一个值是true,则将其转为1再比较;false亦然
5. 一个值是对象,另一个是数字或字符串。则将对象转为原始值,再比较。
对象通过toString()或valueOf()转换为原始值:
toString(): 返回一个反映这个对象的字符串;
valueOf(): 一个对象如果存在一个原始值,就将其转换为它的原始值。
JavaScript语言核心的内置类先尝试使用valueOf(),再尝试使用toString(),除了日期类,日期类只能使用toString转换。
13. for ... in 与 for ... of 的区别
(1) for ... of 是ES6新引入的特性,修复的es5 for ... in 的不足
(2)for...in 循环出的是key, for...of 循环出的是value
(3)for...of 不能循环普通的对象,需要和Object.keys()搭配使用
(4)推荐在循环对象的时候,使用for...in ;在遍历数组的时候使用for... of。
14. js的防抖和节流
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
使用场景:
(1)给按钮加函数防抖防止表单多次提交;
(2)对于输入框连续输入进行AJAX验证时,用函数防抖能有效减少请求次数。
思路: 每次触发事件时,都取消之前的延时调用方法
function debounce(fn, wait){
var timeout = null;
return function (){
if(timeout !== null){
clearTimeout(timeout)
}
timeout = setTimeout(fn, wait)
}
}
// 处理函数
function handle(){
console.log(Math.random())
}
//滚动事件
window.addEventListener('scroll', debounce(handle, 2000))
节流:指连续触发事件但在n秒中只执行一次函数。节流会稀释函数的执行效率。
思路:每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(func, delay){
var prev = Date.now();
return function (){
var context = this;
var args = arguments;
var now = Date.now();
if(now - prev >= delay){
func.apply(context, args);
prev = Date.now();
}
}
}
function handle(){
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 2000));
区别:函数节流不管事件触发有多频繁,都会保证在规定时间内,一定会执行一次真正的事件处理函数;而函数防抖只是在最后一次事件后才触发一次函数。比如:在页面的无线加载情况下,我们需要用户在滚动页面时,每隔一段时间发一次AJAX请求,而不是在用户停下滚动页面操作时才去请求数据,这种场景就适合节流技术来实现。
15.JavaScript全局执行上下文为你做了两件事:全局对象和this关键字
基本执行上下文是全局执行上下文,它是代码中可以随处访问的内容。
16.js进阶整理
for (let i = 1; i < 5; i++) {
if (i === 3) continue
console.log(i)
}
//1 2 4
//当某次结果返回true时,continue跳过本次迭代
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
//3 3 3 和 0 1 2
//js中的setTimeout回调会在遍历结束时执行
//第一个遍历是在var关键字下声明的,就是全局作用下。当i=3时跳出循环,执行三次setTimeout
//第二个遍历是在let关键字下声明,let声明的变量具有块级作用域({}),每次遍历i都有一个新值
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
//20 NaN
//perimeter是一个箭头函数,它的this指向当前周围作用域,即它指向的不是shape,而是window,而window中没有radius属性,返回undefined.
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
//true false false
//new Number()是一个内建的函数构造器,得到一个对象。当使用==操作符时,会检测它里面有没有3这个数字,有就返回true
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = 'green' } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
//TypeError
//colorChange是一个静态方法,而静态方法只能被创建它的构造器使用,不能传给实例。本例中freddie是一个实例,因此会报类型错误
let greeting
greetign = {} // 这两个是我故意打错的
console.log(greetign)
//{}
//比如我们不小心把greeting打错了,但它的返回结果是一个空对象。
//因为我们在全局创建了一个空对象。js解释器将其视为window.greetign={}.变量未经声明就赋值则归全局所有。
//为避免这种情况可以使用严格模式"use strict"
function bark() {
console.log('Woof!')
}
bark.animal = 'dog'
//问题:当我们这么做时,会发生什么?会报错吗?
//答:正常运行
//函数是特殊的对象。函数是一个拥有属性的对象,并且属性可以被调用
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
console.log(member.getFullName());
//TypeError
//不能像常规对象那样给构造函数添加属性,如果想一次性给所有实例添加特性,可以在原型链上写,如下代码:
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
//这样member.getFullName才会有效果
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const lydia = new Person('Lydia', 'Hallie')
const sarah = Person('Sarah', 'Smith')
console.log(lydia)
console.log(sarah)
//Person {firstName: "Lydia", lastName: "Hallie"} 和 undefined
//sarah没有new,那么this引用的是全局对象,而在全局上,sarah是undefined
let number = 0
console.log(number++)
console.log(++number)
console.log(number)
//0 2 2
//num++:返回0, num为1
//++num: 返回2,值自增
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
//“object"
//数组是对象
function getAge() {
'use strict'
age = 21
console.log(age)
}
getAge()
//ReferenceError(引用错误)
var length = 1000
var obj = {
length: 10,
fn: function (fn) {
fn()
arguments[0]()
}
}
function fn() {
console.log(this.length)
}
obj.fn(fn, 1, 2, 3)
//1000 4
// 第一个:fn(), this指向window
// arguments: [fn,1,2,3] => arguments[0]->fn()
// arguments[0]() => 1.fn()()
// 2.this指向arguments => 输出arguments.length
// 上一题的加强版
var len = 10
var obj1 = {
len:6,
method:function(){
console.log(this.len)
}
}
var obj2 = {
len:5,
method:function(fn){
fn();
arguments[0]()
}
}
obj2.method(obj1.method,obj2.method) //10 undefined
//fn(),this指向window, this.len => 10
//arguments[0]() => obj1.method() && this指向arguments,
//但是,arguments没有len属性 => undefined
function Person() {}
var person = new Person()
console.log(person.__proto__ == Person.prototype) //true
console.log(person.__proto__.__proto__ == Object.prototype) //true
console.log(person.__proto__.__proto__.__proto__) // null
console.log(person.__proto__ == Function.prototype) // false
console.log(Object.__proto__ == Function.prototype) // true
//person.__proto__: {constrctor :f}
//person.__proto__.__proto__: Object
//person.__proto__.__proto__.__proto__: null
//Object.__proto__: f
//Object.__proto__ === Function.__proto__ ==== Function.protorype
function fn(arg1, arg2) {
console.log(arg1, arg2) //html css
arguments[0]="duyi"
arg2='web'
console.log(arg1) // duyi
console.log(arguments[1]) // web
}
fn("html", "css")
var a = 0,
b = 0
function A(a) {
A = function(b) {
alert(a + b ++)
}
alert(a++)
}
A(1) //A=function(b){alert(a+b++)} alert(0++) => 1 a=1
A(2) //alert(1+2++) => 4
//答案:1 4
17. 块级作用域与for和if
JavaScript没有块级作用域,但是函数有块级作用域,但是,if 和 for 没有块级作用域(这是js留下的坑)
在JavaScript中,for循环创建的 i 在for循环结束后依然存在外部的执行环境中,因此可以访问到变量i 的值。
for(var i=0;i<5;i++){
var a = 100
}
console.log(a) //100
console.log(i) //5
if(fn){
var c = 'ccc'
}
console.log(c) //ccc
function fn(){
var b = 'bbb'
return b
}
console.log(b) //TypeError: b is not defined
18. round(), ceil(), floor()
Math.round(-1.1) //-1
Math.round(-1.5) //-1
Math.round(-1.6) //-2
Math.round(1.5) //2
Math.ceil(-1.1) //-1
Math.ceil(-1.9) //-1
Math.ceil(1.01) //2
Math.floor(1.99) //1
Math.floor(-1.99) //-2