JavaScript面试指南一

javascript

1.作用域与作用域链

  • 作用域是根据名称查找变量的一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。
  • 当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套,形成了作用域链。其规则就是,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达到全局作用域为止,此时无论是否找到该变量,都会停止查到。

因此,变量可分为本地变量与全局变量:

  1. 全局范围:代码执行前,变量、函数声明会提升
  2. 函数范围:代码执行前,函数、变量,this、argument 会提升。

注意,函数提升由优先于变量提升,函数表达式不会提升。

2.作用域提升

举个例子, var a = 2;JavaScript 引擎会把它分为两个单独的声明,分别是

var a a = 2

第一个是编译阶段的任务,而第二个则是执行阶段的任务。 无论作用域中的声明出现在什么地方,代码执行前首先进行编译处理。 可以将这个过程形象地想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。

3.词法作用域

作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能够知道全部标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对它们进行查找。

4.闭包

4.1什么是闭包

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

4.2闭包有什么用

闭包在实际开发中主要用于封装变量,收敛权限,使用场景:

4.2.1函数作为返回值

	function isFirstLoad(){
      var _list = []//私有变量
      return function(id){
        if (_list.indexOf(id)>=0){
          return false
        }else{
          _list.push(id)
          return true
        }
      }
    }

    var firstLoad = isFirstLoad()
    firstLoad(10) //true
    firstLoad(10) //false
    firstLoad(20) //true
    //在isFirstLoad函数外面,根本不能修改 _list 的值!
复制代码

4.2.2函数作为参数传递:

    var fn;
    function foo() {
     var a = 2;
     function baz() {
     console.log( a );
     }
     fn = baz; // 将 baz 分配给全局变量
    }
    function bar() {
     fn(); // 妈妈快看呀,这就是闭包!
    }
    bar(); // 2
复制代码

将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。

5.原型与原型链

5.1什么是原型

prototype 是个对象,只有函数上有。它是用来存储对象的属性(数据和方法)的地方,是实现JavaScript 原型继承的基础。

5.2原型继承的方式:

// ES6 之前需要抛弃默认的 Bar.prototype Bar.ptototype = Object.create( Foo.prototype ); // ES6 开始可以直接修改现有的 Bar.prototype Object.setPrototypeOf( Bar.prototype, Foo.prototype ); extends Object.getPrototype(); Object.setPrototype()等;

如果忽略掉 Object.create(..) 方法带来的轻微性能损失(抛弃的对象需要进行垃圾回 收),它实际上比 ES6 及其之后的方法更短而且可读性更高。不过无论如何,这是两种完 全不同的语法

5.3原型链

[[Prototype]] 机制就是存在于对象中的一个内部链接,它会引用其他对象。 通常来说,这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就 会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的 引用就会继续查找它的 [[Prototype]],以此类推。这一系列对象的链接被称为“原型链”。

6.this

6.1 this是什么

当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。

6.2 this绑定及优先级

如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后 就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call或者apply(或bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象

ES6 中的箭头函数并不会使用以上四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。

6.3 绑定例外
  • 间接引用

     function foo() { 
         console.log( this.a );
     }
     var a = 2; 
     var o = { a: 3, foo: foo }; 
     var p = { a: 4 };
     o.foo(); // 3
     (p.foo = o.foo)(); // 2
    复制代码

赋值表达式 p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是 foo() 而不是p.foo() 或者 o.foo(),这里会应用默认绑定。

  • 忽略的this

     function foo() { 
      console.log( this.a );
     }
     var a = 2;
     foo.call( null ); // 2
    复制代码

如果你把 null 或者 undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则.

6.4 当this遇到return
  • return的是基本类型:

     function fn()  {  
     this.user = '追梦子';  
     return 1;
     }
     var a = new fn;  
     console.log(a.user); //追梦子
    复制代码
  • return的是对象:

     function fn()  {  
     this.user = '追梦子';  
     return function(){};
     }
     var a = new fn;  
     console.log(a.user); //undefined
    复制代码

如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

6.5 改变this绑定

#####6.5.1 call() 语法:

fun.call(thisArg, arg1, arg2, ...)
复制代码

一、使用call方法调用父构造函数:

    function Product(name, price) {
      this.name = name;
      this.price = price;
    }
    
    function Food(name, price) {
      Product.call(this, name, price);
      this.category = 'food';
    }
    
    var cheese = new Food('feta', 5);//Food {name: "feta", price: 5, category: "food"}
复制代码

二、使用call方法调用函数并且指定上下文的'this'

    function greet() {   
    var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' '); 
    console.log(reply); }
     
    var obj = {   
        animal: 'cats', sleepDuration: '12 and 16 hours'
    };
     
    greet.call(obj);  // cats typically sleep between 12 and 16 hours
复制代码

三、使用call方法调用函数并且没有确定第一个参数,this的值将会被绑定为全局对象,严格模式下this的值将会是undefined。

6.5.2 apply()

语法:

func.apply(thisArg, [argsArray])
复制代码
 /* 找出数组中最大/小的数字 */
var numbers = [5, 6, 2, 3, 7];
/* 应用(apply) Math.min/Math.max 内置函数完成 */
var max = Math.max.apply(null, numbers); /* 基本等同于 Math.max(numbers[0], ...) 
var min = Math.min.apply(null, numbers)
复制代码
6.5.3 bind()

语法:

function.bind(thisArg[, arg1[, arg2[, ...]]])
复制代码

一、创建绑定函数

    this.x = 9;    
    var module = {
      x: 81,
      getX: function() { return this.x; }
    };
    var retrieveX = module.getX;
    retrieveX();   // 返回9 
    
    // 创建一个新函数,把 'this' 绑定到 module 对象
    var boundGetX = retrieveX.bind(module);
    boundGetX(); // 81
复制代码

二、预设的初始参数

function list() {
  return  Array.prototype.slice.call(arguments);
}
var leadingThirtysevenList = list.bind(null, 37);
var list = leadingThirtysevenList(1, 2, 3); 
// [37, 1, 2, 3]
复制代码

7.对象的拷贝

7.1 浅拷贝
7.1.1 赋值=

以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响

const originArray = [1,2,3,4,5];
const cloneArray = originArray;
console.log(cloneArray); // [1,2,3,4,5]
cloneArray.push(6);
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
复制代码
7.1.2 (...)//展开运算符
7.2 深拷贝

完全拷贝一个新对象,修改时原对象不再受到任何影响:

7.2.1 JSON.parse(JSON.stringify(obj))//性能最快
/*
JSON.parse 是将一个 JSON 字符串转成一个 JavaScript 值或对象
JSON.stringify 是将一个 JavaScript 值转成一个 JSON 字符串
*/
const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false
复制代码

注意: 1、具有循环引用的对象时,报错; 2、当值为函数、undefined、或symbol时,无法拷贝;

7.2.2 object.assign()
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
copy.a = 2
console.log(copy) //{ a: 2 }
console.log(obj) //{a: 1}
复制代码
7.2.3 递归进行逐一赋值

8.new运算符的执行过程

  • 新生成一个对象
  • 链接到原型: obj.proto = Con.prototype
  • 绑定this: apply
  • 返回新对象(如果构造函数有自己 retrun 时,则返回该值)

9.代码的复用

当你发现任何代码开始写第二遍时,就要开始考虑如何复用。一般有以下的方式:

  • 函数封装
  • 继承
  • 复制 extend
  • 混入mixin
  • 借用apply/call

10.继承

10.1 最有模式:圣杯模式
//c是目标,p是继承源
var inherit = (function(c,p){
//F函数是一个闭包,仅用做一个缓冲
	var F = function(){};
	return function(c,p){
		F.prototype = p.prototype;
		/*使对象C试图修改自身属性时仅仅是以F函数作为对象进行修改,而不会影响到其他对象 */
		c.prototype = new F();
		//令目标函数c知道自己的原型
		c.uber = p.prototype;
		c.prototype.constructor = c;
	}
})();

复制代码
10.2 ES6语法糖

class/estends

11.数据类型

11.1 类型的7种分类

String、Number、Boolean、object、Symbol、Null、Undefined

  • Undefined:未定义;
  • Null:定义了但为空;
  • String:最大长度为2^53-1,指的是UTF16编码长度;
  • Number:浮点数的比较 console.log(Math.abs(0.1+0.2-0.3)<=Number.EPSILON)
  • Symbol:用于api的扩展
/*运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。*/
    Symbol.prototype.hello = () => console.log("hello");

   var a = Symbol("a");
   console.log(typeof a); //symbol,a 并非对象
   a.hello(); //hello,有效

复制代码
  • Object:内置对象如下 Object,Array,Boolean,Number,String,Function,Data,Regexp,Error.
11.2 类型转换
11.2.1 运算符号转换

JS 中在使用运算符号或者对比符时,会自带隐式转换,规则如下:

  • -、*、/、% :一律转换成数值后计算
  • +:

数字 + 字符串 = 字符串, 运算顺序是从左到右 数字 + 对象, 优先调用对象的valueOf -> toString 数字 +boolean/null -> 数字 数字 + undefined -> NaN

  • 1.toString() === '1'
  • {}.toString() === '[object object]'
  • NaN !== NaN
  • +undefined 为 NaN
  • +null=0,+""=0
  • 加法运算只要其中一个是字符串,那么另外一个也会转换为字符串,然后进行字符串的拼接!
11.2.2 装箱转换

每一种基本类型 Number、String、Boolean、Symbol在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象。

全局的 Symbol 函数无法使用new来调用,但我们仍可利用装箱机制来得到一个 Symbol 对象

    var symbolObject = (function(){ return this; }).call(Symbol("a"));

    console.log(typeof symbolObject); //object
    console.log(symbolObject instanceof Symbol); //true
    console.log(symbolObject.constructor == Symbol); //true

复制代码
11.2.3 ‘== ’ 转换

如果两边类型不同,会有隐式转换发生。总结如下: 1. 首先看双等号前后有没有NaN,如果存在NaN,一律返回false 2. 如果有true或false,转换为1或0,再比较。 3. 如果有引用类型,优先调用valueOf。 4. 数字和字符串,转化为数字再比较。 5. null和undefined,相等。 6. 5,其余都不相等。

	console.log(undefined == false); // false
	console.log(null == false);   // false
	console.log(0 == false);     // true
	console.log(NaN == false);    // false
	console.log("" == false);    // true
	/*
	0 == false之所以为true根据第2条。
    "" == false之所以为true根据第2条,变成了""==0,再根据第4条。*/
console.log([[2]] == '2')   //true
/*
[[2]]的valueOf是对象本身,不是基本类型。尝试调用toString的结果是'2'。因此变成了'2'和数字2的比较。根据第2条,相等*/
复制代码

#####11.2.4 类型转换的6个假值 0/+0,-0,“”,false,undefined,NaN.

11.3 类型判断
  • 基本类型(null): 使用 String(null);

  • 基本类型(string / number / boolean / undefined) + function: 直接使用 typeof即可;

  • 其余引用类型(Array / Date / RegExp /Error): 调用toString

  • 通用但很繁琐的方法:prototype alert(Object.prototype.toString.call(d) === ‘[object Date]') -------> true;

  • 判断封装:

let class2type = {}
'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase()) 

function type(obj) {
    if (obj == null) return String(obj)
    return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj
}
复制代码

12.模块化

通常,我们在浏览器中使用ES6的模块化支持,在 Node 中使用 commonjs 的模块化支持,分类如下:

  • es6: import / export
  • commonjs: require / module.exports / exports
  • amd: require / defined

其中,require与import的区别:

  • require支持 动态导入,import不支持,正在提案(babel下可支持)
  • require是同步导入,import属于异步导入
  • require是值拷贝,导出值变化不会影响导入值;import指向 内存地址,导入值会随导出值而变化

13.ES6/ES7

由于 Babel 的强大和普及,现在 ES6/ES7 基本上已经是现代化开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。

13.1 声明
  • let / const:块级作用域、不存在变量提升、暂时性死区、不允许重复声明
  • const: 声明常量,无法修改
13.2 解构赋值

####13.3 class / extend: 类声明与继承

13.4 Set / Map: 新的数据结构
13.5 异步解决方案:
  • Promise的使用与实现
  • generator: yield: //暂停代码 next(): //继续执行代码
function* helloWorld() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
const generator = helloWorld();
generator.next()  // { value: 'hello', done: false }
generator.next()  // { value: 'world', done: false }
generator.next()  // { value: 'ending', done: true }
generator.next()  // { value: undefined, done: true }

复制代码
  • await / async:是generator的语法糖,babel中是基于promise实现。
 async function getUserByAsync(){
   let user = await fetchUser();
   return user;
}
const user = await getUserByAsync()
console.log(user)
复制代码

14.babel编译原理

  • babylon 将 ES6/ES7 代码解析成 AST(抽象语法树:Abstract Syntax Tree)
  • babel-traverse 对 AST 进行遍历转译,得到新的 AST
  • 新 AST 通过 babel-generator 转换成 ES5

15.函数柯里化

只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

var add = function(x) {
  return function(y) {
    return function(z) {
      return x + y + z;
    }
  }
}

var addOne = add(1);
var addOneAndTwo = addOne(2);
var addOneAndTwoAndThree = addOneAndTwo(3);
console.log(addOneAndTwoAndThree);  //6

复制代码

16.AJAX

16.1 AJAX技术的核心 - XMLHttpRequest对象

XMLHttpRequest对象是浏览器提供的一个API,用来顺畅地向服务器发送请求并解析服务器响应,整个过程中,浏览器页面不会被刷新。

XMLHttpRequest只是一个JavaScript对象,确切的说,是一个构造函数,它是由客户端(即浏览器)提供的(而不是JavaScript原生的),除此之外,它有属性,有方法,需要通过new关键字进行实例化。

16.2 创建一个XML对象的实例:

const xhr = new XMLHttpRequest() 该实例的属性,方法有:

  • 方法
open() //准备启动一个AJAX请求;
setRequestHeader() //设置请求头部信息;
send() //发送AJAX请求;
getResponseHeader() //获得响应头部信息;
getAllResponseHeader() //获得一个包含所有头部信息的长字符串;
.abort() //取消异步请求;
复制代码
  • 属性
responseText //包含响应主体返回文本;
responseXML /*如果响应的内容类型时text/xml或application/xml,该属性将保存包含着相应数据的XMLDOM文档;*/
status //响应的HTTP状态;
statusText //HTTP状态的说明;
readyState //表示“请求”/“响应”过程的当前活动阶段
复制代码

另外,浏览器还为该对象提供了一个onreadystatechange监听事件,每当XML实例的readyState属性变化时,就会触发该事件的发生。其可取的值如下:

0:未初始化 -- 尚未调用.open()方法; 1:启动 -- 已经调用.open()方法,但尚未调用.send()方法; 2:发送 -- 已经调用.send()方法,但尚未接收到响应; 3:接收 -- 已经接收到部分响应数据; 4:完成 -- 已经接收到全部响应数据,而且已经可以在客户端使用了;

16.3 GET请求 与 POST请求
16.3.1 GET

GET请求用于获取数据,有时候我们需要获取的数据需要通过“查询参数”进行定位,在这种情况下,我们会将查询参数追加到URL的末尾,令服务器解析。 const query = "example.php?name=tom&age=24"//"?name=tom&age=24"即是一个查询参数

16.3.2 POST

POST请求用于向服务器发送应该被保存的数据,因此POST请求天然比GET请求多需要一份需要被保存的数据,发送的数据会作为.send()方法的参数最终被发往服务器。

需要注意,.send()方法的参数是不可为空的,也就是说,对于不需要发送任何数据的GET请求,也需要在调用.send()方法时,向其传入null值;

16.4 Open()

.open()方法接收三个参数:请求方式,请求URL地址和是否为异步请求的布尔值。

// 该段代码会启动一个针对“example.php”的GET同步请求。
xhr.open("get", "example.php", false)
复制代码
16.5 一个异步的GET请求代码如下:
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
    if(xhr.readyState === 4 && xhr.status === 200){
        alert(xhr.responseText);
    }
}
xhr.open("get", "example.php", true)
xhr.send(null)
复制代码

注意:为了确保跨浏览器的兼容性,必须要在调用.open()方法之前指定事件处理程序。

17.JavaScript单线程和异步机制

所谓的单线程,即程序的执行顺序就是从上到下依次执行,同一时间内只能执行一段代码。

JavaScript是单线程的,但是浏览器内部是多线程的,其异步也得靠其他线程来监听事件的响应,并将回调函数推入到任务队列等待执行。

单线程所做的就是执行栈中的同步任务,执行完毕后,再从任务队列中取出一个事件(没有事件的话,就等待事件),然后开始执行栈中相关的同步任务,不断的这样循环。

17.1 前端使用异步的场景
  • 定时任务 :setTimeout setInverval
  • 网络请求 :ajax请求,动态加载
  • 事件绑定
	1、定时器
	 console.log(100)
	  setTimeout(function(){
	    console.log(200)
	  },1000)
	  console.log(300)
//100
//300
//200
	2、ajax请求
	  console.log('start')
	  $.get('test.json',function(data){
	    console.log(data)
	  })
	  console.log('end')
	3、动态<img>加载
	  console.log('start')
	  var img = document.createElement('img')
	  img.onload = function(){
	    console.log('load')
	
	  }
	  img.src='https://ss0.baidu.com/'
	  console.log('end')
//start
//end
//load
	4、事件绑定
	  console.log('start')
	  var btn1 = document.getElementById('btn1')
	  btn1.addEventListener('click',function () {
	    console.log('clicked')
	  })
	  console.log('end')
//start
//end
//click

复制代码

18.script标签的defer和async属性

  • async属性

表示后续文档的加载和渲染与js脚本的加载和执行是并行进行的,即异步执行。

  • defer属性

加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),但js脚本的执行需要等到文档所有元素解析完成之后,DOMContentLoaded事件触发前执行。

区别   1.defer和async在网络加载过程是一致的,都是异步执行的;   2.两者的区别在于脚本加载完成之后何时执行,defer更符合大多数场景对应用脚本加载和执行的要求;   3.如果存在多个有defer属性的脚本,那么它们是按照加载顺序执行脚本的;而对于async,它的加载和执行是紧紧挨着的,无论声明顺序如何,只要加载完成就立刻执行。

DOM

1.BOM与DOM区别与关联

1.1 BOM
  • BOM是Browser Object Model的缩写,即浏览器对象模型。
  • BOM没有相关标准。
  • BOM的最根本对象是window。 从1可以看出来:BOM和浏览器关系密切。浏览器的很多东西可以通过JavaScript控制的,例如打开新窗口、打开新选项卡(标签页)、关闭页面,把网页设为主页,或加入收藏夹,等等…这些涉及到的对象就是BOM。

从2可以看出来:由于没有标准,不同的浏览器实现同一功能,可以需要不同的实现方式,就要考虑浏览器兼容性了。

1.2 DOM
  • DOM是Document ObjectModel的缩写,即文档对象模型。
  • DOM是W3C的标准。
  • DOM最根本对象是document(实际上是window.document)。

从1可以看出来:DOM和文档有关,这里的文档指的是网页,也就是HTML文档,和浏览器无关。

2.DOM事件模型

javascript中有两种事件模型:DOM0,DOM2。

2.1 DOM0级事件模型

分为两种

  • 行内事件:在标签中写事件
  • 元素.on事件名=函数

1.行内

<input type="button" id="btn" value="按钮" onclick="alert('hello world!')"> 
复制代码

2.元素.on事件名=函数

document.getElementById("btn").onclick = function () {
alert('hello world!');
} 
复制代码

注意,一个dom对象只能注册一个同类型的函数,注册多个同类型的函数的话,就会发生覆盖,之前注册的函数就会无效。

2.2 DOM2级事件模型
2.2.1 事件捕获和事件冒泡

事件冒泡:当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在所有祖先元素中被触发。

事件捕获和事件冒泡机制如下图

2.2.2 DOM2级的注册事件和解除事件

在DOM2级中使用addEventListener()removeEventListener()来注册和解除事件(IE8及之前版本以下用attachEvent()添加事件和detachEvent()删除事件)。DOM2不会发生事件的覆盖,会依次的执行各个事件函数。   addEventListener('事件名称','回调','捕获(true)/冒泡(flase)')。示例如下:

<div id = 'outer' >
    <div id="inner" ></div>
</div>
<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(event){
        alert('inner show');
        event.stopPropagation();
    },false);
    clickouter.addEventListener('click',function(){
        alert('outer show');
    },false);
</script>

复制代码

一般情况下,我们在不添加stopPropagation()阻止冒泡函数时,点击inner,会先弹出inner,再弹出outer。添加了stopPropagation()函数之后,执行完inner的事件函数之后,就不会在执行outer的事件函数了

由于事件捕获阶段没有可以阻止事件的函数,所以一般都是设置为事件冒泡。

3. 事件委托

事件委托就是利用事件冒泡机制指定一个事件处理程序,来管理某一类型的所有事件。 即:利用冒泡的原理,把事件加到父级上,触发执行效果。 好处:

  • 只在内存中开辟了一处空间,节省资源同时减少了dom操作,提高性能
  • 动态添加的元素也绑定着事件。 例如,用ul中触发li来改变他们的背景颜色
<ul id='ul'>
    <li>111111</li>
    <li>222222</li>
    <li>333333</li>
 </ul>
 <button id='button'>添加元素</button>
 
 window.onload = function(){
      let oUl = document.getElementById('ul');
      let aLi = oUl.getElementsByTagName('li');
      let but = document.getElementById('button');
      let now = 3;
      oUl.onmouseover = function(e){
        let ev = e || window.event;
        let target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
          target.style.background = 'red';
        }
      }
      oUl.onmouseout = function(e){
        let ev = e || window.event;
        let target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
          target.style.background = '';
        }
      }
      but.onclick = function(){
        now ++;
        let newLi = document.createElement('li');
        newLi.innerHTML = 111111 * now;
        oUl.appendChild(newLi);
      }
    }
复制代码

WEB

1.HTTP 状态码

常用的状态码。 • 200表示服务端成功响应。 • 301表示永久重定向。 • 302表示临时重定向。 • 403表示请求被拒绝。 • 404表示服务端找不到请求的资源。 • 500表示处理请求出错。503表示服务不可用 ,504 表示网关超时。

2.HTTP缓存

2.1 HTTP报文

HTTP报文就是浏览器和服务器间通信时发送及响应的数据块。 主要分为两部分 1.包含属性的首部(header)--------------------------附加信息(cookie,缓存信息等) 2.包含数据的主体部分(body)-----------------------HTTP请求真正想要传输的部分

2.2 HTTP缓存

http缓存可以理解为在服务端和客户端之间一个缓存数据库,你只需要设置一些参数即可实现,比如缓存/不缓存/时效内缓存/时效外缓存等(默认存在缓存)。缓存规则主要分为两类,即强制缓存和对比缓存。

2.2.1 强制缓存

对于强制缓存来说,响应header中会有两个字段来标明失效规则Expires/Cache-Control

  1. Expires: HTTP 1.0的内容,作用基本忽略。
  2. Cache-Control Cache-Control 是最重要的规则。常见的取值有private、public、no-cache、max-age,no-store,默认为private。
  • private:客户端可以缓存
  • public: 客户端和代理服务器都可缓存
  • max-age=xxx:缓存的内容将在 xxx 秒后失效
  • no-cache:需要使用对比缓存来验证缓存数据(一般都设置为该值,使用缓存前都判断文件是否为最新,更为合理。)
  • no-store:所有内容都不会缓存,强制缓存,对比缓存都不会触发
2.2.2 对比缓存

对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。 可分为2种标识:

  1. Last-Modified / If-Modified-Since

Last-Modified:第一次请求时,服务器返回资源的最后修改时间; If-Modified-Since:再次请求时,浏览器通知服务器,上次请求时返回资源的最后修改时间。

如果Last-Modified小于等于If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码200;否则,响应HTTP304,告知浏览器继续使用所保存的cache。

  1. Etag/If-None-Match(优先级高于Last-Modified/If-Modified-Since)

Etag:第一次请求时,服务器返回的资源唯一标识符(生成规则由服务器决定) If-None-Match:再次请求服务器时,浏览器通知服务器缓存数据的唯一标识。

如果Etag和If-None-Match不同,说明资源又被改动过,则响应整片资源内容,返回状态码200;否则,响应HTTP304,告知浏览器继续使用所保存的cache。

浏览器第一次请求:

浏览器再次请求:

总之,对于强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。 对于比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

3.http缓存方案

3.1 md5/hash缓存

通过不缓存html,为静态文件添加MD5或者hash标识,解决浏览器无法跳过缓存过期时间主动感知文件变化的问题

webpack提供了webpack-md5-hash插件,可以帮助我们在项目发布时自动修改文件标识。

3.2 CDN缓存

CDN缓存提供了分流以及访问加速等优势条件,是可以通过登录,手动更新CDN缓存的,变相解决了浏览器缓存无法手动控制的问题。

3.3 浏览器操作对HTTP缓存的影响

4.从URL到页面显示的过程

浏览器在得到用户请求之后,经历了下面这些阶段:重定向→拉取缓存→DNS查询→建立TCP链接→发起请求→接收响应→处理HTML元素→元素加载完成

1、首先,在浏览器地址栏中输入url

2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。

3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。

4、浏览器通过三次握手与远程Web服务端来建立一个TCP/IP连接

5、握手成功后,浏览器向服务器发送http请求,请求数据包。

6、服务器处理收到的请求,将数据返回至浏览器

7、浏览器收到HTTP响应

8、读取页面内容,浏览器渲染,解析html源码

9、生成Dom树、解析css样式、js交互

10、客户端和服务器交互

5.web会话跟踪的方法

HTTP是一种无状态的协议,为了分辨链接是谁发起的,需自己去解决这个问题。不然有些情况下即使是同一个网站每打开一个页面也都要登录一下

5.1 Cookie

Cookies是服务器在本地机器上存储的小段文本,即访问后网站的相关信息,它随每一个请求发送至同一服务器,是在客户端保持状态的方案。 Cookie的主要内容包括:名字,值,过期时间,路径和域 ,数据大小不能超过4k。

5.2 sessionStorage 和 localStorage

与cookie类似,但不会自动把数据发给服务器,仅在本地保存,数据大小可达5M或以上。

• localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
• sessionStorage 数据在当前浏览器窗口关闭后自动删除。
复制代码
5.3 URL重写

就是首先获得一个进入的URL请求然后把它重新写成网站可以处理的另一个URL的过程。

5.4 隐藏input

隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用。浏览者单击发送按钮发送表单的时候,隐藏域的信息也被一起发送到服务器。

6.GET 和 POST 的区别

GET和POST是HTTP协议中发送请求的两种方法,它们的区别,简单的说: GET产生一个TCP数据包;POST产生两个TCP数据包。

也就是说:对于GET方式的请求,浏览器会把http的header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

  1. GET与POST都有自己的语义,不能随便混用。
  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

具体差异可表现以下5个方面:

  • 缓存上,GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • 编码方式上,GET请求只能进行url编码,而POST支持多种编码方式。
  • 参数长度上,GET请求在URL中传送的参数是有长度限制的,而POST没有。
  • 参数的数据类型上,GET只接受ASCII字符,而POST没有限制。
  • 参数传递上,GET参数通过URL传递,POST放在Request body中,更安全。

6.网站性能优化

可分为三个方面,网络传输性能、页面渲染性能以及JS阻塞性能

6.1 网络传输性能优化
6.1.1浏览器缓存:在服务器上设置的Etag字段。

在浏览器接收到服务器响应后,会检测响应头部(Header),如果有Etag字段,那么浏览器就会将本次缓存写入硬盘中。 注意,在构建阶段,需要为我们的静态资源添加md5 hash后缀,避免资源更新而引起的前后端文件无法同步的问题。

6.1.2资源打包压缩:用webpack进行自动化打包编译

对webpack进行上线配置时,我们要特别注意以下几点:

  1. JS压缩: new webpack.optimize.UglifyJsPlugin()
  2. HTML压缩:
  • 使用html-webpack-plugin自动化注入JSCSS打包HTML文件
  • 书写HTML元素的src 或 href 属性时,可以省略协议部分
  1. 提取公共资源: new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'scripts/common/vendor-[hash:5].js' })
  2. 提取css并压缩 extract-text-webpack-plugin
  3. 服务器上开启Gzip传输压缩,但注意,不要对图片文件进行Gzip压缩
6.1.3图片资源优化

1.不要在HTML里缩放图像 2.使用雪碧图(CSS Sprite) 自动化生成雪碧图的工具 3.使用字体图标(iconfont); 4.使用WebP 是可以加快图片加载速度的图片格式,叉拍云 5.使用cdn 同时使用DNS预解析技术DNS Prefetch

6.2页面渲染性能优化

主要是降低重排和重绘的频率和成本

1.CSS属性读写分离

2.通过切换class或者使用元素的style.csstext属性去批量操作元素样式。

3.DOM元素离线更新 例如display:none对元素隐藏,在元素“消失”后进行相关操作。

4.将没用的元素设为不可见:visibility: hidden

5.压缩DOM的深度 一个渲染层内不要有过深的子元素,少用DOM完成页面样式,多使用伪元素或者box-shadow取代。

6.图片在渲染前指定大小 因为img元素是内联元素,所以在加载图片后会改变宽高,严重的情况会导致整个页面重排,所以最好在渲染前就指定其大小,或者让其脱离文档流。

6.3 js阻塞性能

在编程的过程中,如果我们使用了闭包后未将相关资源加以释放,或者引用了外链后未将其置空(比如给某DOM元素绑定了事件回调,后来却remove了该元素),都会造成内存泄漏的情况发生,进而大量占用用户的CPU,造成卡顿或死机。我们可以使用chrome提供的JavaScript Profile进行测试。

7.浏览器的渲染机制

CSS

1.CSS盒模型

HTML元素的显示呈现为一个矩形,CSS标准中称之为Box。一个HTML元素占据空间的大小由盒模型决定。在盒模型中,一个盒子由内容content、内边距padding、边框border和外边距margin共同构成,其尺寸也是这四部分的尺寸之和。 盒模型是有两种标准: • 标准模型--盒模型的宽高只是内容(content)的宽高, • IE模型中--盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高。

2.css reset 和 normalize.css 有什么区别?

Normalize.css只是一个很小的css文件,它在HTML元素样式上提供了跨浏览器的高度一致性。相比于传统的CSS reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案。 Normalize vs Reset

  • Normalize.css 保护了有价值的默认值

Reset通过为几乎所有的元素施加默认样式,强行使得元素有相同的视觉效果。相比之下,Normalize.css保持了许多磨人的浏览器样式。这就意味着你不用再为所有公共的排版元素重新设置样式。当一个元素在不同的浏览器中有不同的默认值时,Normalize.css会力求让这些样式保持一致并尽可能与现代标准符合。

  • Normalize.css 修复了浏览器的bug

它修复了常见的桌面端与移动端浏览器的bug。这往往超出了Reset所能做到的范围。关于这一点,Normalize.css修复的问题包含了HTML5元素的显示设置、预格式化文字的font-size问题、在IE9中SVG的溢出、许多出现在各浏览器和操作系统中的与表单相关的bug。

  • Normalize.css 不会让你的调试工具变的杂乱

使用Reset最让人困扰的地方莫过于在浏览器调试工具中大段大段的继承链,在Normalize.css中就不会有这样的问题。

  • Normalize.css 是模块化的

这个项目已经被拆分为多个相关却又独立的部分,这使得你能够很容易也很清楚地知道哪些元素被设置了特定的值。因此这能让你自己选择性地移除掉某些永远不会用到的部分(比如表单的一般化)。

  • Normalize.css 拥有详细的文档

3.BFC

3.1 定义:

BFC 全称为 block formatting context,中文为“块级格式化上下文” 如果一个元素具有 BFC,内部子元素再怎么翻江倒海、翻 云覆雨,都不会影响外部的元素。

3.2 触发 BFC 常见的情况如下:
  • 根元素;
  • float 的值不为 none;
  • overflow 的值为 auto、 scroll 或 hidden;
  • display 的值为 table-cell、 table-caption 和 inline-block 中的任何一个;
  • position 的值不为 relative 和 static。
3.3 用途:
  • BFC 元素是不会发生 margin 重叠的;
  • BFC 元素可以用来清除浮动的影响: .lbf-content { overflow: hidden; } /* IE7+ */
  • 实现自适应布局 普通流体元素在设置了 overflow:hidden 后,会自动填满容器中除了浮动元素以外的剩余空间,形成自适应布局效果.

4.居中

4.1 行内元素
  1. 父元素设置text-align: center;(只设置水平居中) 优点:是不用计算子元素尺寸。

  2. 子元素通过absolute配合transform()(同样适用于块级元素)

.child{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
复制代码

优点:不用计算当前元素尺寸。 3. 子元素直接使用 absolute(同样适用于块级元素)

.child{
    width: 100px;
    height: 100px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px; /*高度的一半*/
    margin-left: -50px; /*宽度的一半*/
  }
复制代码

注意,需要计算子元素宽高. 4. 父元素使用flex布局(同样适用于块级元素)

//方案一:配合使用 justify-content和 align-items属性
.father{
    display: flex;
    justify-content: center; /*水平居中*/
    align-items: center; /*垂直居中*/
  }
  //方案二:子元素只用margin属性,如果浏览器不兼容flex属性,有回退作用
  .father{
    display: flex;
  }
  .child{
    margin: auto;
  }
复制代码
4.2 块级元素
  1. 使用margin:0 auto配合元素的width
#center{
    width: 100px;
    margin: 0 auto;
}

复制代码

注意,当元素处于position:absolute;时,margin:0 auto无效,需要将right于left都设为0才可以,如下所示

#center{
    width: 100px;
    margin: 0 auto;
    position:absolute;
    right:0;
    left:0;
}

复制代码

法2.3.4.同上

4.3 垂直居中

  1. 通过line-height
#main{
    height: 200px;
    line-height: 200px;
}

复制代码

缺点:需要固定父元素的height值,并且居中元素如果是多行文本将错乱。仅适合小元素,单行文本。

法2.3.4.同上。

5.清除浮动

两大基本方法

  • 元素添加伪类
.clearfix:after{content:'';display:table;clear:both} //ie8+
.clearfix{*zoom:1;}
复制代码

2.父元素BFC

	.lbf-content { overflow: hidden; }//IE7+
复制代码

6.css优先级计算规则

6.1 选择器类型
  • id选择器(#myid)、
  • 类选择器(.myclassname)、属性选择器(a[rel=”external”])、伪类选择器(a:hover, li:nth-child)
  • 标签选择器(h1)、伪元素(::before)
  • 相邻选择器(h1 + p)、
  • 子选择器(ul > li)、
  • 后代选择器(li a)、
  • 通配符选择器(*)、
6.2 优先级

ID选择器 > 类选择器|属性选择器|伪类 > 标签|伪元素 > 通配符

css规则由选择器和声明块组成:

css优先级就是通过选择器的特殊值计算的,选择器的特殊性值表述为4个部分,用0,0,0,0表示:

  • ID选择器的特殊性值,加0,1,0,0。
  • 类选择器、属性选择器或伪类,加0,0,1,0。
  • 标签和伪元素,加0,0,0,1。
  • 通配选择器*对特殊性没有贡献,即0,0,0,0。
  • 最后比较特殊的一个标志!important(权重),它没有特殊性值,但它的优先级是最高的,可以认为它的特殊性值为1,0,0,0,0。
a{color: yellow;} /*特殊性值:0,0,0,1*/
div a{color: green;} /*特殊性值:0,0,0,2*/
.demo a{color: black;} /*特殊性值:0,0,1,1*/
.demo input[type="text"]{color: blue;} /*特殊性值:0,0,2,1*/
.demo *[type="text"]{color: grey;} /*特殊性值:0,0,2,0*/
#demo a{color: orange;} /*特殊性值:0,1,0,1*/
div#demo a{color: red;} /*特殊性值:0,1,0,2*/
复制代码

选择器特殊性值是从左向右排序的,也就是1,0,0,0大于以0开头的所有特殊性值,行间样式的特殊性是1,0,0,0,比ID选择器优先级高。

6.3 层叠

假如特殊性相同的两条规则应用到同一个元素,css会先查看规则的权重(!important),加了权重的优先级最高;当权重相同的时候,css规则会按顺序排序,后声明的规则优先级高。

例如,伪类,:link、:visited、:hover、:active,都遵循“爱恨原则LVHA”(LoVe HAte),特殊性值相同,后声明的规则优先级高,就可以覆盖前面的。

7.gif/png/jpge

HTML

1. 标签分类

• 文档元信息:通常是出现在head标签种,包含了描述文档自身的一些信息,诸如 title、meta、style、link、base ; • 语义相关:扩展了纯文本,表达文章结构、不同语言要素的标签,诸如 section、nav 的标签; • 链接:提供到文档内和文档外的的链接; • 替换型标签:引入声音、图片、视频等外部元素替换自身的一类标签; • 表单:用于填写和提交信息的一类标签; • 表格:表头、表尾、单元格等表格的结构。

2.HTML 语义化

语义化标签是纯文本的补充,比如标题,自然段,章节等,这些内容是纯文字无法表达的,我们就需要语义标签表达.

正确使用优点如下: • 便于团队维护和开发 • 去掉css样式后网页结构完整 • 便于阅读机器阅读,适合搜索引擎检索seo

相反,错误地使用语义标签,会给机器阅读造成混淆、增加嵌套,给 Css编写加重负担。 但现代互联网产品里,HTML用于描述“软件界面”多过于“富文本",而软件界面里的东西,实际上几乎是没有语义的。比如说,我们做了一个购物车功能,一定要用给商品套上ul吗?比如,加入购物车的按钮,一定要用button吗?

实际上,我认为是没必要,因为这个场景里,跟文本的列表以及表单的button,已经相差很远了。所以,在软件界面,可以直接用div和span。

3.meta viewport 是做什么用的,怎么写?

在移动浏览器中使用viewport元标签控制布局,包含以下内容:

<meta name="viewport" content="with=device-with,initial-scale=1,maximum-scale=1">
复制代码

4.html5为什么只需要写<!DOCTYPE>

HTML5不基于SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为 所以,html5只有一种:<!DOCTYPE> 但是html4.01有三种,分别是strict、transitional、frameset。

5.html5新特性、移除元素,HTML5新标签的浏览器兼容

实现上:h5不再是SGML的子集。 1、新特性:主要是关于图像,位置,存储,多任务等功能的增加,如: • 绘画canvas • 用于媒介回放的video和audio元素 • 本地离线存储localStorage,长期存储,浏览器关闭之后数据不丢失 sessionStorage的数据在浏览器关闭后自动删除 • 语意化更好的内容元素,比如 article、footer、header、nav、section • 表单控件,calendar、date、time、email、url、search; • 新的技术webworker, websocket, Geolocation; 2、移除的元素: 纯表现的元素:basefont,big,center,font, s,strike,tt,u; 对可用性产生负面影响的元素:frame,frameset,noframes; 3、处理兼容性: • IE8/IE7/IE6支持通过document.createElement方法产生的标签,可以利用这一特性让这些浏览器支持HTML5新标签,浏览器支持新标签后,还需要添加标签默认的样式。 • 使用html5shim,可以让IE9或更低版本能支持html5 <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>

6.行内元素、块级元素、空(void)元素

• 行内元素有:a,b,span,img(我曾以为是block),input,strong,select
• 块级元素有:div、ul(无序)、ol(有序)、li、p等
• 空元素:<br><hr><link><script>
空元素定义:html元素的内容就是其两个标签之间的content,所以,标签之间没有内容的就是空元素
复制代码

7.页面导入样式时,使用link@import的区别

写法上:

<link rel="stylesheet" href="路径" />
<style type="text/css">
    @import '路径'
</style>
复制代码

本质上:link属于XHTML标签,除了加载css之外,还能定义RSS,定义rel连接属性等作用。而@import是css提供的,只能用于加载css; 解析上:link是跟着页面加载同时加载的,但是@import会等到页面加载完再加载 兼容上:@import IE5以上才能识别,link无限制

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值