前端面试 知识大盘点

JavaScript

作用域和作用域

执行上下文

  • 范围: 一段<script>或一个函数
  • 全局: 变量定义,函数声明 一段<script>
  • 函数: 变量定义,函数声明,this,arguments 函数

作用域

jsES6前无块级作用域

// Hoisting
if(true){
  var name = 'wangcai'
}

函数(私有)作用域和全局作用域

var a=100;
function fn(){
    var a = 200;
    console.log(fn,a);
}
console.log('global',a);
fn();

作用域链

// 通俗地讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。
var a= 100;
function f1(){
    var b = 200;
    function f2(){
        var c = 300;
        console.log(a);
        console.log(b);
        console.log(c);
    }
}

私有作用域执行顺序

正常来讲程序自上往下执行

  • 形参赋值

    1. 全局环境不能直接访问局部环境
    2. 如果function(私有作用)没有形参赋值, 私有作用域中变量无声明修饰符,该变量就上级作用域找赋值;如果有形参赋值,私有作用域不会变量提升.
  • 变量提升

    1. 变量提升: 只是提升变量的声明,并不会把赋值也提升上来。
    2. var a;在 全局作用域下声明的变量,也是相当于给window设置了一个对象属性,而且两者之间建立了映射的机制 <==> window.a=undefined;
    3. 方法变量提升是会把函数体提上上来
    4. let, const声明不存再变量提升,如果let,const同一作用域存在重复声明,浏览器有自我检测机制,代码(一开始)直接报错,不存在解释执行
fn()
function fn(){console.log(1)}
fn()
function fn(){console.log(2)}
fn()
var fn = 10
fn()  //报错, 下面就不会再执行
function fn(){console.log(3)}
fn()
function fn(){console.log(4)}
fn()
//最终输出: 4 4 4 fn is not function

//exp2
let a=10,b=10;
function fn(){
// console.log(a,b); //会报错, a is not defined
let a = b =20;    // 这样写语法 ===  let a =20; b = 20;
console.log(a,b);
}
fn()
console.log(a,b)
//输出:10 10
//    10 20

// exp3
var a = 12,b= 13,c = 14;
function fu(a){
/****
* 1.形参赋值
*    a = 12
* 2.变量提升
*    var b;
* => 在私有作用域中, 只有下面两种情况是私有变量
*      A:声明过的变量 (带var/function)
*      B:形参也是私有变量
*    剩下的都不是自己的私有变量,都需要基于作用域链的机制向上查找
*/
console.log(a,b,c);
var b = c = a = 20;
console.log(a,b,c);
}
fn(a) // 执行就是把全局a的**值**12 当实参传递给函数的形参 ==>fn(12),
      // 注意:如果传的是引用类型,传递的值是栈内存地址
console.log(a,b,c);

//exp 4
var ary = [12,13];
function fn(ary){
console.log(ary);  // =>[12,13]
// 虽然这里的ary 是私有的, 但其指针还是与公有的ary是同一个对内存数据
ary[0]=100;
ary = [100];  // 这里是重新赋值了,指针改变
ary[0] = 0;
console.log(ary)  //=>[0]c
}

fn(ary); // 引用类型,传递的值是栈内存地址
console.log(ary);  // =>[100,13]

in:检测某一个属性是否隶属的于这个对象(不管是私有属性还是共有属性,只要有这个属性结果就是true
hasOwnProperty:检测某一个属性是否隶属的于这个对象(只有这个属性是私有的才可以)

var a;
console.log("a" in window);
var v='Hello World';
alert(v); //Hello World
var v='Hello World';
(function(){
    alert(v); //hello world
})()
var v='Hello World';
(function(){
    alert(v); //undefined
    var v='I love you';
})()
等同
var v='Hello World';
(function(){
    var v; // 变量提升
    alert(v); //undefined
    v='I love you';
})()
// 当解析器读到 if 语句的时候,它发现此处有一个变量声明和赋值,
// 于是解析器会将其声明提升至当前作用域的顶部(这是默认行为,并且无法更改),
// 这个行为就叫做 Hoisting。
var a = 1;
function foo() {
  if (!a) {
    var a = 2;
  }
  alert(a);
};
foo();
// 等同
var a = 1;
function foo() {
  var a;
  if (!a) {
    a = 2;
  }
  alert(a);
};
foo();
(function(){
    var a='One';
    var b='Two';
    var c='Three';
})()
// 等同
(function(){
    var a,b,c;
    a='One';
    b='Two';
    c='Three';
})()

函数提升:
function是可以在被执行的位置之后定义,JS实际运行时会首先将它提升到顶部
函数提升是把整个函数都提到前面去。只有函数才会创建新的作用域(函数声明才会被提升,表达式声明不会被提升)

// 在我们写js code 的时候,我们有2中写法,一种是函数表达式,另外一种是函数声明方式。
// 我们需要重点注意的是,只有函数声明形式才能被提升。
// 函数声明方式提升【成功】
// 代码如下:
function myTest(){
    foo();
    function foo(){
        alert("我来自 foo");
    }
}
myTest();

// 函数表达式方式提升【失败】
// 代码如下:
function myTest(){
    foo();
    var foo =function foo(){
        alert("我来自 foo");
    }
}
myTest();  //foo is not a function


当前函数执行,形成一个私有作用域A,A的上级作用域是谁,和他再那执行没有关系, 和他在哪创建(定义)的有关系, 在哪创建的,他的上级作用域就是谁!

var a = 1;
function fn(){
  console.log(a)
  var a = 5
  console.log(a)
  a++
  var a
  fn3()
  fn2()
  console.log(a)
  function fn2(){
    console.log(a)
    a = 20
  }
}
function fn3(){
  console.log(a)
  a = 200
}
fn()
console.log(a)
//输出
undefined
5
1
6
20
200

循环事件绑定

循环事件绑定
循环事件绑定2

严格模式

arguments 是一个对应于传递给函数的参数(实参)的类数组对象,映射关系(赋值瞬间关联),能建立映射的就建立映射,不能建立的,后面不管怎么操作都无法关联;
⭐在严格模式下不存在映射

"use strict"
function fn(x,y,z){
var arg = arguments;
console.log(x);  // ==> 10
arg[1] = 30;
// 这里再赋值是没有用的
// 虽然argments:
//  0:10
//  1:30
//  length:1 ⭐没错还是1

// ....
// 但与形参不会再映射
console.log(arg);
console.log(y);  // ==> undefined
y=40;
console.log(arg[1]);// 还是==30;
}
fn(10);
/**
*  argments:
*  0:10
*  length:1
~function(){
    "use strict"
    function fn(x){
        arguments[0]=100;
        console.log(x); // => 10 不存在映射
    }
    fn(10);
}();

*  callee:指向当前执行的函数
*  ...
**/
"use strict"
function getInfo(name, age, sex){
  console.log('name:',name);
  console.log('age:', age);
  console.log('sex:', sex);
  console.log(arguments);
  arguments[0] = 'valley';
  console.log('name', name);
}
getInfo('Tim', 2, '男');
//name: Tim
//age: 2
//sex: 男
//{
//  0: Tim,
//  1: 2,
//  2: 男,
//  length: 3,
//  callee: f(),
//}
//name: Tim ==>非严格模式下是 valley

第一,严格模式下无法再意外创建全局变量


逻辑计算

// 1. 逻辑或赋值 false 取 或后面值
var a = false || 1; // =>1

// 2. 逻辑与相反
var b = false && 2; // =>false

// 逻辑与一般回调函数中用到
function fn(callback){
// 如果传递的值是个函数, 我们才让其执行
// if(typeof callback === "function"){
//        callback;
// }

// 简写版(不严谨,但用的多)
// 默认callback要不然就传函数,要不然就不穿
  callback && callback();
}
fn(function(){
  // xxx
})

3.逻辑与和逻辑或的混合模式
// 优先级: 逻辑与的优先级高于逻辑或

方法执行运算

注意数组的参数引用

image

1.非严格模式下自行函数如果不是.出来的 就是属于window

var b = 2;
var c = 5 + (b++); // c=7; b=2;  这里注意了b++加不加括号都是顺序计算 这里括号没意义

类:具备某些共同特征的实体的集合

实例:某一个类别具体的一个事务
js有5中简单数据类型(也称为基本数据类型): UndefinedNullBooleanNumberString。还有1中复杂的数据类型————ObjectObject本质上是由一组无序的名值对组成的。
其中UndefinedNullBooleanNumber都属于基本类型。ObjectArrayFunction则属于引用类型,String有些特殊:
因为字符串具有可变的大小,所以显然它不能被直接存储在具有固定大小的变量中。由于效率的原因,我们希望JS只复制对字符串的引用,而不是字符串的内容。但是另一方面,字符串在许多方面都和基本类型的表现相似,而字符串是不可变的这一事实(即没法改变一个字符串值的内容),因此可以将字符串看成行为与基本类型相似的不可变引用类型

var x = 1;
var y = x;
x.name = 'hanna'; // 基本类型无法添加属性
console.log(y); //1
console.log(x.name); //undefined

typeof 会根据对象类型返回对应的类型字符串, 但是有几个缺点:
对于数组、函数、对象来说,其关系错综复杂,使用 typeof 都会统一返回 “object” 字符串,
nullobject
NaNnumber
undefinedundefined
那么此时我们有第二个方法可以使用, 是 ES3中的 Object.prototype.toString方法,我们可以用Object.prototype.toString.call(obj)检测对象类型:

何时使用 =====

if(obj.a==null){
    // 这里相当于 obj.a == null || obj.a == undefined , 简写形式
    // 这是Jquery 源码中推荐写法, 其他都用 ===

    // 注意 == 不能用于 未定变量判断
if(xxx==null){}   //报错 xxx is not defined

if 判断转换只有 0、NaN、空字符串、nullundefined会转换为false,其他都为true

console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(Math));//[object Math]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]

为什么得用Object.prototype.toString.call,因为toStringObject的原型方法,而ArrayFunction等类型作为Object的实例,都重写了toString方法,不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。

作用域

原型链

必背

prototype : 显示原型

__proto__ : 隐式原型

function Fn(){
    this.x = 100;
    this.y = 200;
    var z = 300;
    this.getX = function(){
        console.log(this.x);
    }
}
Fn.prototype.y = 500;
Fn.prototype.getX = function(){
    console.log(this.x);
}
Fn.prototype.getY = function(){
    console.log(this.y);
}
var f1 = new Fn; // 当没传参时,写不写()都行
var f2 = new Fn();
console.log(f1.x) //undefined 只有函数类的this. 属性或方法 实例才能继承
console.log(f1.getX === f2.getX)  // false  两个不同的实例的 **私有** 方法
console.log(f1.getY === f2.getY)  // true  如果私有属性没有 就往上级查找
console.log(f1.__proto__.getY === Fn.prototype.getY)  // true
console.log(f1.__proto__.getX === F2.getX) // false
console.log(f1.constructor) // FN;  f1是实例没有constructor,往上查找 prototype.constructor ==> Fn;
console.log(Fn.prototype.__proto__.constructor); //  Fn.prototype.__proto__ === Object.prototype;
f1.getX();// 100  this:f1 ==>console.log(f1.x) 
f1.__proto__.getX() // undefined f1.__proto__  === Fn.prototype
f2.getY();// 200  this:f2 ==>console.log(f2.y) 
Fn.prototype.getY() // 500
Fn.prototype.getX() // undefined
image

原型重定向

EXP1

function fun(){
    this.a = 0;
    this.b = function(){
        alert(this.a);
    }
}
fun.prototype = {
    b: function(){
        this.a = 20;
        alert(this.a);
    },
    c: function(){
        this.a = 30;
        alert(this.a);
    }
}
var my_fun = new fun();
my_fun.b(); // 0
my_fun.c(); // this => my_fun.a = 30 ; 30
// 结果:0 30

my_fun.a 用来设置私有属性
my_fun.__proto__.a 用来设置公有属性

为什么要重定向原型

项目开发中方便批量扩展属性与方法的时候.

原型重定向导致的问题

  1. 自己开辟的堆内存中没有constructor属性,导致类的原型构造函数缺失(解决:自己手动在堆内存中增加constructor属性)
  2. 当原型重定向后,浏览器默认开辟的那个类原型堆内存会被释放掉,如果之前已经存储了一些方法或属性,都会丢失(所以:内置累的原型不允许重定向到自己开辟的堆内存,因为内置类的原型上存在很多属性方法,重定向后都没了,这样是不被允许的;但浏览器对内置类有保护机制)
    私有属性: 自己对内存中存储的属性,相对自己来说是私有
    公有属性: 自己基于proto找到的属性,相对自己来说是公有的

关于this的两道题

this指向判断 MDN的解释:当前执行代码的环境对象

  1. 元素绑定事件,方法中的this是当前操作的元素
  2. 方法名签是否有点,有点,点前面是谁this就是谁,没有thiswindow(严格模式下是undefined)
  3. 构造函数执行,方法中的this是当前类的一个实例

全局环境,this就代表Window对象。

var name = 'zhar';
function say(){
  console.log(this.name);//zhar
}
say();

对象环境, this指向对象本身。

var obj = {
  name : "zhar",
  say : function(){
    console.log(this.name);//zhar
  }
}
obj.say();

构造函数环境, this会指向创建出来的实例对象

function Person() {
    this.name = 'zhar';
}
var p = new Person();
console.log(p.name);

事件对象,在DOM事件中使用thisthis指向了触发事件的 DOM 元素本身

li.onclick = function(){
    console.log(this.innerHTML);
}

EXP1

var fullName = 'language';
var obj = {
    fullName: 'javascrtpt',
    prop: {
        getFullName: function(){
            return this.fullName;
        }
    }
};
console.log(obj.prop.getFullName()); // this => obj.prop; obj.prop没有fullName属性; undefined
var test = obj.prop.getFullName;
console.log(test()); // this => window.fullName(非严格模式); language

EXP2

var name = 'window';
var Tom = {
    name: 'Tom',
    show: function(){
        console.log(this.name); // 4. 'window'
    },
    wait: function(){
        var fun = this.show; // 2. => this: Tom
        fun(); // 3. => this: window
    }
};
Tom.wait(); // 1. => this: Tom

this碰到return

function fn()
{
    this.user = '小J';
    return {};
}
var a = new fn;
console.log(a.user); //undefined

function fn()
{
    this.user = '小J';
    return function(){};
}
var a = new fn;
console.log(a.user); //undefined

function fn()
{
    this.user = '小J';
    return 1;
}
var a = new fn;
console.log(a.user); //小J

function fn()
{
    this.user = '小J';
    return undefined;
}
var a = new fn;
console.log(a.user); //小J

//如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
function fn()
{
    this.user = '小J';
    return undefined;
}
var a = new fn;
console.log(a); //fn {user: "小J"}

//还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn()
{
    this.user = '小J';
    return null;
}
var a = new fn;
console.log(a.user); //小J

call apply

是什么? Function原型上的方法
作用? 改变this的指向

exp

// 数组拼接
var a1 = [1,2,3]; var a2 = [4,6,5];
[].push.apply(a1,a2);

// 类型版判
Object.prototype.toString.apply(new Date);

// 数组最大小值
Math.max.apply(null,a1);

// 位数组的转换
var a ={0:'xxx',1:'xxxx',2:'xx',length:3};
var b = [].slice.call(a);
b.forEach(i=>console.log(i));

// 构造继承
function A(){}
function B(){
  A.apply(this,arguments)
}
// 简便分割字符串
// 原: 分割成数组再转换
console.log('abc'.split(',').join(','))

=>  // ⭐ 当使用apply 或 call 传入的第一个值传入的简单类型时,
    // 会自动转换包装对象
console.log([].join.call('abc',','))
[].forEach.call('abc',i=>{
    console.log(i)
})

// 一般框架 严格模式下兼容问题
(function(){
    'use strict'
}).call(this)

npm

nrm是专门用来管理和快速切换私人配置的registry

建议全局安装

npm install nrm -g --save

nrm有一些默认配置,用nrm ls命令查看默认配置,带*号即为当前使用的配置

nrm ls

也可以直接输入以下命令查看当前使用的是哪个源

nrm current

切到源http://r.cnpmjs.org/,命令:nrm use 源的别名,即

nrm use cnpm


模块化

优点

  1. 避免命名冲突(减少全局命名空间污染);
  2. 更好的分离,按需加载;
  3. 代码复用;
  4. 高可维护维护.

全局函数模式:将不同的功能封装成不同的全局函数(最原始的写法)

缺点:命名冲突,全局污染,其他js可随意访问修改变量

let msg = "module1"
function foo(){
  console.log('foo()',msg)
}
function bar(){
  console.log('foo()',msg)
}

namspace模式: 简单对象封装

缺点:其他js还是可随意访问修改变量

let obj = {
    msg:'module2',
    foo:function(){
      console.log("xxxx")
    }
}

IIFE模式:匿名函数自调用(闭包) 立即执行函数

// jquer写法
(function(win){
  let msg = 'xxasdf';
  function fn(){
    console.log('fn()',msg);
  }
  win.module3 = {fn};
})(window)

IIFE模式增强:引用依赖: 现代模块实现的基石

问题:

  1. 请求过多
  2. 依赖模糊
  3. 难以维护
//
(function(win,$){
  let msg = "module4";
  function foo(){
    console.log('foo()',msg);
  }
  win.module4 = foo;
  &('body').css('backgruond','red');
})(window,jQuery);

CommonJS Nodejs 使用

每个文件可当做一个模块

前端打包工具出现,CommonJS前端也可以使用

AMD requsit

AMDRequireJS 在推广过程中对模块定义的规范化产出。
CMDSeaJS 在推广过程中对模块定义的规范化产出。
类似的还有 CommonJS Modules/2.0 规范,是 BravoJS 在推广过程中对模块定义的规范化产出。
还有不少⋯⋯
这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。
目前这些规范的实现都能达成浏览器端模块化开发的目的。

区别:

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.

  2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:

    // CMD
    define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此处略去 100 行
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
    // ...
    })
    
    // AMD 默认推荐的是
    define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething()
    // 此处略去 100 行
    b.doSomething()
    ...
    })
    

虽然AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。

  1. AMDAPI 默认是一个当多个用,CMDAPI 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 requireCMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。

ES6 出现,想同一现在所有模块化标准

模块化只是代码的写法问题, 没必要弄这么多标准, 标准是越统一越好,越简单越好.
语法:import export(注意有无default)
环境:babel编译ES6语法,

模块化工具: Webpack(功能强大)始于2012年,rollup(功能专一,打包文件比较小,Vue,React)

Webpack 始于2012年,由 Tobias Koppers发起,用于解决当时现有工具未解决的的一个难题:构建复杂的单页应用程序(SPA)。特别是 Webpack 的两个特性改变了一切:

  1. 代码拆分(Code Splitting) 使你可以将应用程序分解成可管理的代码块,可以按需加载,这意味着你的用户可以快速获取交互性的网站,而不必等到整个应用程序下载和解析完成。当然你可以手动来完成这项工作,那么祝你好运。
  2. 静态资源(Static assets) 如图像和 CSS 可以导入到你的应用程序中,而且还能够被作为依赖图中的另一个节点。再也不用关心你的文件是否放在正确的文件夹中,再也不用为文件 URL 增添 hash 而使用 hack 脚本,因为 Webpack 会帮我们处理这些事情。

Rollup 则是由于不同的原因被创建的:利用 ES2015 巧妙的模块设计,尽可能高效地构建出能够直接被其它 JavaScript 库引用的模块。其他的模块打包工具 – 包含 Webpack – 通过都是将每个模块封装在一个函数中,将它们放在一个包中,通过浏览器友好的 require 实现,最后逐一执行这些模块。如果您需要按需加载,webpack 这类的打包工具非常合适。否则有点浪费,如果你有很多模块,它会变得更糟。

ES2015模块启用了一种不同的方法,这是真是 Rollup 使用的。所有的代码都放在同一个地方,然后一次性执行,从而生成更简洁、更简单的代码,从而启动更快。您可以 自己使用 Rollup REPL 来查看它 。

但是有一个权衡:代码拆分(Code Splitting)是一个更加棘手的问题,在撰写本文时,Rollup 还不支持。同样的,Rollup 也不支持模块的热更新HMR。而对于使用 Rollup 的人来说,最大的痛点可能是 – 它能处理大多数 CommonJS 文件(通过 插件 ),然而有些东西根本不能转译为 ES2015 ,而 Webpack 能处理所有你丢给它的事情。

抉择: 对于应用使用 Webpack,对于类库使用 Rollup

Webpack HMR 热更新实现流程

  1. Webpack编译期,为需要热更新的 entry 注入热更新代码(EventSource通信) → 最新的webpack-dev-server,改成用WebSocket协议了,基于sockjs
  2. 页面首次打开后,服务端与客户端通过 EventSource 建立通信渠道,把下一次的 hash 返回前端
  3. 客户端获取到hash,这个hash将作为下一次请求服务端 hot-update.jshot-update.jsonhash
  4. 修改页面代码后,Webpack 监听到文件修改后,开始编译,编译完成后,发送 build 消息给客户端
  5. 客户端获取到hash,成功后客户端构造hot-update.js script链接,然后插入主文档
  6. hot-update.js 插入成功后,执行hotAPIcreateRecordreload方法,获取到 Vue 组件的 render方法,重新 render 组件, 继而实现 UI 无刷新更新。

虚拟MOD


MVVM

  • Model-模型、数据
  • View - 视图、模板(视图和模型是分离的)
  • ViewModel - 连接Model和View(连接器,桥的作用),View可以通过事件绑定印象到Model,Model可以通过数据绑定影响到View

MVVM是MVC的一种创新,MVC主要用于后端


组件化 模块化

组件化 模块化
就是"基础库"或者“基础组件",意思是把代码重复的部分提炼出一个个组件供给功能使用。 就是"业务框架"或者“业务模块",也可以理解为“框架”,意思是把功能进行划分,将同一类型的代码整合在一起,所以模块的功能相对复杂,但都同属于一个业务。
使用:Dialog,各种自定义的UI控件、能在项目或者不同项目重复应用的代码等等。 使用:按照项目功能需求划分成不同类型的业务框架(例如:注册、登录、外卖、直播.....)
目的:复用,解耦。 目的:隔离/封装 (高内聚)。
依赖:组件之间低依赖,比较独立。 依赖:模块之间有依赖的关系,可通过路由器进行模块之间的耦合问题。
架构定位:纵向分层(位于架构底层,被其他层所依赖) 架构定位:横向分块(位于架构业务框架层)。

总结

  • 组件相当于库,把一些能在项目里或者不同类型项目中可复用的代码进行工具性的封装。
  • 而模块相应于业务逻辑模块,把同一类型项目里的功能逻辑进行进行需求性的封装。

vue

Vue与jQuery区别

vue 数据与视图分离, 以数据驱动视图,只关心数据变化,DOM操作备份装, jQuery 直接在代码操作DOM.

VUE 三要素

  1. 响应式:vue如何监听到Data的每一个属性变化? → Object.defineProperty

        var obj = {};
        var _name='张三';
        Object.defineProperty(obj,"name",{
            get:function(){
                console.log('get',_name);    // 监听
                return _name;
            },
            set:function(newVal){
                console.log('set',_name);    // 监听
                _name = newVal;
            }
        });
    
        //Vue 监听模拟
        var vm = {}
        var data = {
            name: 'zhangsan',
            age: 20
        }
        var key, value
        for (key in data) {
            (function (key) {
                Object.defineProperty(vm, key, {
                    get: function () {
                        console.log('get', data[key]) // 监听
                        return data[key]
                    },
                    set: function (newVal) {
                        console.log('set', newVal) // 监听
                        data[key] = newVal
                    }
                })
            })(key)
        }
    
  2. 模板引擎: vue的模板是如何解析,指令如何处理,模板是什么?

    render函数执行解析模板 vnode

    • 本质:字符串
    • 有逻辑,如 v-if v-for
    • HTML格式很像,但有很大区别
    • 最终还是要转换为HTML来显示
  3. 渲染:vue的模板如何渲染成HTML? 以及渲染过程

    • render函数执行vnode
    • UpdateComponentpatch

Vue 实现流程

第一步 解析Vue模板字符串成render函数, 模板中用到的data中的属性,都变成了JS变量模板中的 v-model v-for v-on 都变成了JS逻辑,render函数执行后返回vnode

第二步 响应式开始监听, 用Object.defineProperty修改变量的get/set方法,实现响应式,开始监听,并将 data 的属性/methods的方法等代理到 vm 上(从data.list代理到vm.list上,这样render函数才能用到那个变量with(this){...list}// vm.list

第三步:首次渲染,执行render函数,获得vnodepatch转化成真实DOM,显示页面,且通过get收集订阅者,绑定依赖(执行 render 函数,会访问到 vm.list vm.title,会被响应式的 get 方法监听到,只有被get监听到的data才会在set的时候响应式更新,data中有很多属性,有些被用到,有些可能不被用到,避免不必要的重复渲染)

第四步:data 属性变化,被响应式set监听到(上一步的data,不被get监听到的,这一步不会被set监听到,即怎么修改都不会变化),触发updateComponent,重新执行render函数,生成新的vnode,与旧vnode执行diff算法对比,重新patch更新到html页面中

简化

  1. 解析模版,通过render函数生成vnode格式的JS
  2. 响应式监听数据变化(getter,setter)
  3. 渲染页面,绑定依赖
  4. data数据变化,触发re-render更新视图

React vs Vue

本质区别

  1. Vue - 本质是 MVVM 框架,由 MVC 发展而来
  2. React - 本质是前端组件化框架,由后端组件化发展而来

但这并不妨碍他们两者都能实现相同的功能

模板区别

  1. Vue - 使用模板(最初由angular 提出)
  2. React - 使用 JSX (模板与js混在一起,未分离)
  3. 模板语法上,我更加倾向于 JSX
  4. 模板分离上,我更加倾向于 Vue

组件化的区别

  1. React 本身就是组件化,没有组件化就不是 React
  2. Vue 也支持组件化,不过是在 MVVM 上的扩展
  3. 查阅 Vue 组件化的文档,洋洋洒洒很多(侧面反映)
  4. 对于组件化,我更加倾向于 React ,做的彻底而清晰

两者共同点

  1. 都支持组件化
  2. 都是数据驱动试图

国内使用,首推 vue 。文档更易读、易学、社区够大
如果团队水平较高,推荐使用 React 。组件化和 JSX

hybrid

  • hybrid 是客户端和前端的混合开发
  • hybrid 存在的核心意义在于快速迭代,无需审核
  • hybrid 实现流程: 服务端做好静态页面包,客户端检查包版本,如果客户端版本低,就获取服务端静态包, 客户端拿到前端静态页面,以文件形式存储在app
    客户端 使用 file 协议 通过 webview 加载静态页面

使用 NA :体验要求极致,变化不频繁(无头条的首页)

使用 hybrid :体验要求高,变化频繁(如头条的新闻详情页)产品型

使用 h5 :体验无要求,不常用(如举报、反馈等页面) 运维型

优点: 体验好,快速迭代,无需审核
缺点: 开发成本高,运维成本高

JS与客端通信

String 自带函数

substr()含头含尾(废弃),substring()含头不含尾,slice()也是含头不含尾


JS 执行过程 ⭐⭐

event loop 事件循环机制 单线程 任务队列
先由上到下执行同步任务,异步任务挂起,等同步任务处理后再执行异步任务(如果同步任务没有结束→死循环,异步任务是不会执行的.)

  • 主线程 由上到下执行 (同步任务)
  • 异步任务:
    1. 微任务: Promise mutationObserver interSectionObserver
    2. 宏任务: setTimeout setInterval requestAnimationFrame

JS为什么是单线程?

避免DOM渲染冲突

  • 浏览器需要渲染DOM
  • JS 可以修改DOM
  • JS执行的时候,浏览器DOM渲染会暂停
  • 两段JS也不能同时执行(都修改DOM就冲突了)
  • webworker 支持多线程,但不能访问DOM

同步异步区别:

  • 同步会阻塞代码(alert),异步不会(setTimeout)

异步问题

  1. 没按书写方式执行,可读性差
  2. callback中不容易模块化
setTimeout(function(){
    console.log(1)
},0);
new Promise(r=>{
    console.log(2);
    r();
    console.log(3);
}).then(data=>{
    console.log(4);
});
console.log(5);
//  2 3 5 4 1


$.ajax({
    url:'xxx',
    success:function(){
        console.log(1)
    }
})

setTimeout(function(){
    console.log(2)
},1000)

setTimeout(function(){
    console.log(3)
})
console.log(4)

// 这里分两种情况,如果ajax请求数据的数据在1内就返回了就
// 4,3,1,2
// 如果请求的数据没在1内返回
// 4,3,2,1

Promise ⭐⭐

Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口。简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。它起到代理作用(proxy),充当异步操作与回调函数之间的中介,使得异步操作具备同步操作的接口。Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。

特点

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变

为什么要使用Promise
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数; 解决回调地狱(异步).

原型方法:

  1. Promise.prototype.then(resolved,rejected)

注意点:

  • then有两个参数,第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
  • then返回的是一个新的 Promise, 将以回调的返回值来resolve.
  1. Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

  1. Promise.prototype.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

面试题

///实现一个简单的Promise

function Promise(fn){
  var status = 'pending'
  function successNotify(){
      status = 'fulfilled'//状态变为fulfilled
      toDoThen.apply(undefined, arguments)//执行回调
  }
  function failNotify(){
      status = 'rejected'//状态变为rejected
      toDoThen.apply(undefined, arguments)//执行回调
  }
  function toDoThen(){
      setTimeout(()=>{ // 保证回调是异步执行的
          if(status === 'fulfilled'){
              for(let i =0; i< successArray.length;i ++)    {
                  successArray[i].apply(undefined, arguments)//执行then里面的回掉函数
              }
          }else if(status === 'rejected'){
              for(let i =0; i< failArray.length;i ++)    {
                  failArray[i].apply(undefined, arguments)//执行then里面的回掉函数
              }
          }
      })
  }
  var successArray = []
  var failArray = []
  fn.call(undefined, successNotify, failNotify)
  return {
      then: function(successFn, failFn){
          successArray.push(successFn)
          failArray.push(failFn)
          return undefined // 此处应该返回一个Promise
      }
  }
}

HTML

块元素 大多为结构性标记

特点

  1. 独占一行(从上到下排版);
  2. 可以设置Css盒子模型所有属性;再不设置宽高时,宽继承父级,宽有内容决定;
  3. 可以嵌套其他元素(p不能嵌套p,dt不能嵌套其他元素)
属性名 描述
section 文档节
nav 导航
header 页眉
article 文章
aside 文章侧栏
footer 页脚
div
h1 ~ h6 标题
p
ul > li 无须列表
ol > li 有序列表
dl > dt > dd 自定义列表
table > tr > td 表格
form 表单

行类元素 大多为描述性标记

特点

  1. 从左到右一行显示;
  2. 不可以设置宽高;(但可设置marginpadding左右值)
  3. 不设置宽高时,宽高是又内容决定的
  4. 编辑代码时,行内元素出现回车或者换行时,会默认又间隙(解决:父级(body)设置 font-size:0)
  5. 不可以嵌套块级元素
  6. 基线对齐问题

元素

属性名 描述
span 内联容器
a
strong b
em i
label

行类块级元素

特点

  1. 从左到右一行显示;
  2. 可以设置Css盒子模型所有属性;不设置宽高时,宽高是又内容决定的
  3. 编辑代码时,行内块级元素出现回车或者换行时,会默认又间隙(解决:父级(body)设置 font-size:0)

元素
| 属性名 | 描述 |
|img|
|input|
|textarea| 文本域|
|audio|
|video|

视口概念

<meta id="viewport" name="viewport"
content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;">
属性名 取值 描述
width 正整数 或?device-width 定义视口的宽度,单位为像素
height 正整数 或?device-height 定义视口的高度,单位为像素,一般不用
initial-scale [0.0-10.0] 定义初始缩放值
minimum-scale [0.0-10.0] 定义缩小最小比例,它必须小于或等于maximum-scale设置
maximum-scale [0.0-10.0] 定义放大最大比例,它必须大于或等于minimum-scale设置
user-scalable yes/no 定义是否允许用户手动缩放页面,默认值yes

Css

盒子模型

标准盒子模型

高宽只有content

IE盒子模型(怪异模型)

高度是border+padding+content

设置方法:

/* 盒子模型最终宽高默认是不计算 边框与内边距的
* content-box:最终宽高 = 内容的宽高
* border-box:最终宽高 = border + padding + 内容的宽高
*/
box-sizing:content-box; //默认
box-sizing:border-box;  //IE模型

margin-top传值问题:
块级元素嵌套时,当父级元素没有设置padding-topborder-top时,自己元素设置了margin-top值时,这个值会直接传递父级元素
解决方法:
父级元素设置:overflow:hidden,超出隐藏;
margin兼容问题:
一个元素设置了margin-top,另一个元素设置了margin-bottom,这两个元素不会相加,会取最大值(这两个元素时同级,平级关系的时候,⭐ BFC解决)

三角形

//实心
div {
    width: 0;
    height: 0;
    border: 40px solid;
    border-color: transparent transparent red;
}
// 箭头三角形
#blue:after {
    content: "";
    position: absolute;
    top: 2px;
    left: -38px;
    border-width: 0 38px 38px;
    border-style: solid;
    border-color: transparent transparent #fff;
}

js 获取盒子模型对于宽高
dom.style.width/height
or
dom.currentStyle.width/height
or
window.getComputedStyle(dom).width/heigth

定位

相对定位 relative

当盒子本身发生位置改变时,又不影响其他元素,就用相对定位

  1. 不脱离文档流
  2. 当位置发生改变时,原来位置还在占用
  3. 层级大于文档流内的其他元素(会覆盖在其他元素之上)
  4. 参照物是本身
  5. 给绝对定位当作参照物(常用)
  6. 同时设置topbottom值时 top生效,同时设置leftrightleft生效

绝对定位 absoluto

  1. 不设置参照物时,参照物是body

  2. 人为设置参照物时

    • 参照物必须时父级元素
    • 父级元素必须带有定位属性
    • 同级之间不能设为参照物
    <div style ="position:relative;"> <!--  <--- 这个才是参照物 -->
        <div style = "position:relative;"></div>
        <div>
            <div style = "position:absolute;"></div>
        </div>
    </div>
    
  3. 脱离文档流的

  4. 不设置四个方向值时,这个绝对以为元素前面有其他元素,就会默认排在这个元素的后面

  5. 同时设置topbottom值时 top生效,同时设置leftrightleft

  6. 当设置了宽高为100%时,将继承参照物宽高

改变定位的层级关系 z-index

  1. 当定位属性时平级时,哪个元素在上面就设置哪个属性的z-index值(前提时设置定位属性)
  2. 当定位的父级元素同时设置z-index值时,子元素与父元素相比较时不生效的.

display

行类块级元素转换

  • block 块级元素

    1. 独占一行
    2. 可设置宽高
    3. 可以嵌套
  • inline: 行内

    1. 不可设置宽高
    2. 有间隙
    3. 基线对齐问题
    4. 只能从左到右
  • inline-block: 行内块

    1. 有间隙
    2. 基线对齐问题
    3. 只能从左到右
  • float:浮动

    1. 脱离文档流(父级找不到子集),相当于来到了第二层级,平行默认文档流
    2. 不在设置宽高时,宽高有内容决定
    3. 所有元素都可以设置float 无论是img,a,span,div...
    4. 设置是浮动属性,这个元素相当于是行内块级元素(可以设置宽高)
    5. 行类元素、行内块级元素和文字围绕着浮动元素排布(图文混编)

清除float: 面试点

  1. 设置父级高度(不常用),不管子集有没有元素,高度一定的.
  2. 给父级元素设置overflow:hidden; 把子集元素拉回到文档流内
  3. 清除浮动clear:both,必须保证三个前提
    • 使用这个这个元素必须是块级元素
    • 使用这个属性的元素必须放再最后一个浮动元素后置,使用:after伪类搞定
    • 使用这个属性的元素不能带有float属性
.clear:after{
    display:block;
    content:"";
    clear:both; /**高版浏览器**/
}
.clear{
    *zoom:1;/**兼容低版本浏览器**/
}

BFC

就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

BFC``布局规则:

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Boxmargin会发生重叠
  3. 每个元素的margin box的左边, 与包含块border
  4. box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  5. BFC的区域不会与float box重叠。
  6. 计算BFC的高度时,浮动元素也参与计算

<iframe height="300" style="width: 100%;" scrolling="no" title="LodVrQ" src="//codepen.io/luotian/embed/LodVrQ/?height=300&theme-id=31054&default-tab=css,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href='https://codepen.io/luotian/pen/LodVrQ/'>LodVrQ</a> by luotian
(<a href='https://codepen.io/luotian'>@luotian</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>


DOM事件

  1. Dom事件级别(版本)

Dom0 element.onclick = function(){}
Dom2 element.addEventListener('click',function(){},false)
Dom3 element.addEventListener('keyup',function(){},false)

Dom1 制订的时候没有设计与事件相关的东西,所有没有Dom1 ,Dom2与3写法没差别,Dom3 多了许多事件类型; true:冒泡,false:捕获

  1. Dom事件模型(捕获,冒泡)

捕获:上到下 (windowdocumenthtmlbody→目标元素)
冒泡:下到上 (目标元素→ bodyhtmldocumentwindow

  1. Dom事件流

事件流:捕获(阶段)→ 目标阶段→冒泡(阶段)

  1. Dom捕获流程(参考2)

  2. Event对象常用应用

    • event.preventDefault():阻止默认行为(a标签:设置了click事件,阻止其默认跳转)
    • event.stopPropagetion():阻止冒泡
    • event.stoplmmediatePropagetion():事件优先级设置
    • event.currentTarget: 当前绑定事件元素,父级元素 (事件代理) ⭐
    • event.target:获取具体点击元素 ⭐
  3. 自定义事件

    //与其他事件结合使用
    var eve = new Event('test');
            ev.addEventListener('test', function () {
                console.log('test dispatch');
            });
    
    setTimeout(function () {
        ev.dispatchEvent(eve);  //调用是对象,不是名称
    }, 1000);
    

HTTP 协议类 参考

特点

  1. 简单快速:每个资源都是固定的,直接请求获取
  2. 无连接:不会保持连接,HTTP 0.91.0使用非持续连接:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。HTTP 1.1使用持续连接:不必为每个web对象创建一个新的连接,一个连接可以传送多个对象,采用这种方式可以节省传输时间
  3. 无状态:单次http协议是不能区分连接者身份,无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
  4. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。

HTTP报文

    A(请求报文 Requst)-->B(请求行)
    A--> C(请求头 -- key/value值请求参数)
    A--> D(空行)
    A--> E(请求体)
    B--> F(http方法)
    B--> G(页面地址)
    B--> H(http协议)
    B--> I(http协议版本)
    F--> J(GET - 获取资源)
    F--> K(POST - 传输资源)
    F--> L(PUT -更新资源)
    F--> M(DELETE - 删除资源)
    F--> N(HEAD -获取报文首部)

    Z{响应报文 Respose}--> Y(状态行)
    Z-->X(响应头)
    Z-->T(空行)
    Z-->S(响应体)
    Y-->R(协议/协议版本)
    Y-->Q(状态码)
    Y-->P

GET与POST区别 99%的人都理解错了HTTP中GET与POST的区别

  1. GET在浏览器回退时是无害的,而POST会再次提交请求。⭐
  2. GET产生的URL地址可以被Bookmark,而POST不可以。
  3. GET请求会被浏览器主动cache,而POST不会,除非手动设置。⭐
  4. GET请求只能进行url编码,而POST支持多种编码方式。
  5. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  6. GET请求在URL中传送的参数是有长度限制的,而POST么有。⭐
  7. 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  8. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。⭐
  9. GET参数通过URL传递,POST放在Request body中⭐

GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。

HTTP 状态码

  • 1xx: 指示信息-表请求已经接收,继续处理

  • 2xx: 成功 - 请求已被成功接收

    200.OK: 客户端请求成功

    206.Partial Content:客户发送一个带有Range头的GET请求,服务器完成了它 (某部分,播放视频音频文件)

  • 3xx: 重定向 - 要完成请求必须进行更进一步的操作

    301.Moved Permanenly:永久重定向, 所请求的页面已经转移至新的url

    302.Found:临时重定向 所有请求页面已经零时转移至新的url

    304.Not Modified:客户端缓存可以继续使用, 客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来的缓存的文档还可以继续使用

  • 4xx: 客户端错误 - 请求哟语法错误或请求无法实现

    400.Bad Requset 客户端请求有语法错误,不能被服务器理解

    401.Unauthorized 请求未授权,这个状态码必须和WWW-Authenticate 报文域一起使用

    403.Forbidden 服务器收到请求,但是拒绝提供服务(不能直接访问,只能服务器去访问)

    404.Not Found 请求资源不存在

  • 5xx: 服务器错误 - 服务器未能实现合法的请求

    500.Internal Server Error 服务器错误,发生了不可预期的错误,原理来的缓存的文档还可以继续使用

    503.Server Unavailable 请求未完成, 服务器零时过载或宕机,一段时间后可能恢复正常

HTTP 持久连接与管线化 (版本1.1后)

  1. 什么是持久连接? (keep alive模式)

    HTTP1.1规定了默认保持长连接(HTTP persistent connection,也有翻译为持久连接);数据传输完成了保持TCP连接不断开(不发RST包、不四次握手),等待在同域名下继续用这个通道传输数据;相反的就是短连接。

    HTTP 1.1版本支持持久连接 1.0版本不支持

    与非持久连接的区别:

    持久连接使客户端到服务器端连接持续有效,避免了重新建立连接

    大大减少了连接的建立以及关闭时延。HTTP连接是建立在TCP协议之上的,建立一条TCP连接需要三次握手,TCP连接关闭时需要四次挥手。这些都是需要时间的。

  2. 什么是管线化 → 客户端并行发送请求,服务器并发多个响应

    管线化机制须通过永久连接(persistent connection)完成,仅HTTP/1.1支持此技术(HTTP/1.0不支持)

    在使用持久连接的情况下,某个连接消息的传递类似于

    请求1 → 响应1 → 请求2 → 响应2

    管线化:某个连接上的消息变成了类似这样

    请求1 → 请求2 → 请求3 → 响应1 → 响应2 → 响应3

  • 那么持久连接和管线化的区别在于:持久连接的一个缺点是请求和响应式是顺序执行的,只有在请求1的响应收到之后,才会发送请求2,而管线化不需要等待上一次请求得到响应就可以进行下一次请求。实现并行发送请求
  • 只有GET和HEAD要求可以进行管线化,而POST则有所限制
  • 初次创建连接时也不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
  • HTTP1.1要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败,而且现在很多服务器端和代理程序对管线化的支持并不好,现代浏览器Chrome和Firefox

跨域

只要协议、域名、端口有任何一个不同,就是跨域。

为什么不能跨域?
浏览器有一个同源策略,用来保护用户的安全。

跨域的解决方案

  1. JSONP 原理:script标签引入一个js文件,下载文件

    • 优点:兼容性好,在很古老的浏览器中也可以用,简单易用,支持浏览器与服务器双向通信。
    • 缺点:只支持GET请求,且只支持跨域HTTP请求这种情况(不支持HTTPS)
  2. CORS 一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

  3. 服务器代理.

  4. H5 PostMessage

    // postMessage
    // 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
    Bwindow.postMessage('data', 'http://B.com');
    // 在窗口B中监听
    Awindow.addEventListener('message', function (event) {
        console.log(event.origin);
        console.log(event.source);
        console.log(event.data);
    }, false);
    
  5. iframe hash

    // 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
    // 在A中伪代码如下:
    var B = document.getElementsByTagName('iframe');
    B.src = B.src + '#' + 'data';
    // 在B中的伪代码如下
    window.onhashchange = function () {
        var data = window.location.hash;
    };
    
  6. Websocket【参考资料】http://www.ruanyifeng.com/blog/2017/05/websocket.html

       var ws = new WebSocket('wss://echo.websocket.org');
       ws.onopen = function (evt) {
           console.log('Connection open ...');
           ws.send('Hello WebSockets!');
       };
       ws.onmessage = function (evt) {
           console.log('Received Message: ', evt.data);
           ws.close();
       };
       ws.onclose = function (evt) {
           console.log('Connection closed.');
       };
    

原型链

创建对象有几种方法

// 第一种方式:字面量
var o1 = {name: 'o1'};
var o2 = new Object({name: 'o2'});

// 第二种方式:构造函数,任何函数都可以用作于构造函数,
// 只要去用New关键字去操作生成新的实例对象,这个函数就是构造函数
var M = function (name) { this.name = name; };
var o3 = new M('o3');
// 第三种方式:Object.create
var p = {name: 'p'};
var o4 = Object.create(p);

M.prototype.say = function () {
    console.log('say hi');
};
var o5 = new M('o5');

// new 运算符原理
var new2 = function (func) {
    var o = Object.create(func.prototype);
    var k = func.call(o);
    if (typeof k === 'object') {
        return k;
    } else {
        return o;
    }
};

console.log(o3 instanceof M)// true
console.log(o3 instanceof Object)// true

什么是原型链:

一个实例对象向上找构造该实例的相关联对象,该相关联的对象又往上找创造他的它的原型对象,以此类推,一直到object.prototype,这个链条就是原型链

作用:
公用方法

面向对象

 /**
 * 类的声明
 */
var Animal = function () {
    this.name = 'Animal';
};

/**
 * es6中class的声明
 */
class Animal2 {
    constructor () {
        this.name = 'Animal2';
    }
}

/**
 * 实例化
 */
console.log(new Animal(), new Animal2());

/**
 * 借助构造函数实现继承
 * 缺点: 无法继承 父类的prototype的公有方法
 */
function Parent1 () {
    this.name = 'parent1';
}
Parent1.prototype.say = function () {

};
function Child1 () {
    Parent1.call(this);
    this.type = 'child1';
}
console.log(new Child1(), new Child1().say());

/**
 * 借助原型链实现继承
 * 缺点: 父类的应用类型属性是公用的,都可以修改
 */
function Parent2 () {
    this.name = 'parent2';
    this.play = [1, 2, 3];
}
function Child2 () {
    this.type = 'child2';
}
Child2.prototype = new Parent2();

var s1 = new Child2();
var s2 = new Child2();
console.log(s1.play, s2.play);
s1.play.push(4);

/**
 * 组合方式
 */
function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
}
function Child3 () {
    Parent3.call(this);
    this.type = 'child3';
}
Child3.prototype = new Parent3();
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);

/**
 * 组合继承的优化1
 * @type {String}
 */
function Parent4 () {
    this.name = 'parent4';
    this.play = [1, 2, 3];
}
function Child4 () {
    Parent4.call(this);
    this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();
console.log(s5, s6);

console.log(s5 instanceof Child4, s5 instanceof Parent4);
console.log(s5.constructor);

/**
 * 组合继承的优化2
 */
function Parent5 () {
    this.name = 'parent5';
    this.play = [1, 2, 3];
}
function Child5 () {
    Parent5.call(this);
    this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);


⭐⭐⭐
function Elem(id){
    this.elem = document.getElementById(id);
}
Elem.prototype.on = function(type,fn){
    var elem = this.elem;
    elem.addEventListener(type,fn);
    return this;
}
Elem.prototype.html = function(hl){
    var elem = this.elem;
    if(hl){
        elem.innerHTML = hl;
        return this;
    }
    else{
        return elem.innerHTML;
    }
}

var em = new Elem("ad_t2");
em.on('click',function(){console.log('click click')})
  .html("<div>yooo~</div>")
  .on('mouseover',function(){console.log("mouse mouse over voer....")});

js Class与普通构造函数有何区别

  • js构造函数
function MathHandle(x,y){
    this.x = x
    this.y = y
}

MathHandle.prototype.add = function(){
    return this.x +  this.y
}

var m = new MathHandle(1,2)
console.log(m.add())
  • class基本语法
class MathHandle{
    constructor(x,y){
        this.x = x
        this.y = y
    }
    add(){
        return this.x + this.y
    }
}

const m = new MathHandle(1,2)
console.log(m.add())

三. 语法糖

在上述两段代码中分别加入如下代码,运行。

console.log(typeof MathHandle) // 'function'
console.log(MathHandle.prototype.constructor === MathHandle) //true
console.log(m.__proto__ === MathHandle.prototype) //true

运行结果一致。我认为,class是构造函数的语法糖。

四、继承

  1. 构造函数形式的继承

    //动物
    function Animal(){
        this.eat = function (){
            console.log('Animal eat')
        }
    }
    
    //狗
    function Dog() {
        this.bark = function (){
            console.log('Dog bark')
        }
    }
    
    Dog.prototype = new Animal()
    
    var hashiqi = new Dog()
    hashiqi.bark()
    hashiqi.eat()
    
  2. class继承

    class Animal {
        constructor(name){
            this.name = name
        }
        eat(){
            alert(this.name + ' eat')
        }
    }
    
    class Dog extends Animal {
        constructor(name){
            super(name) //super就是被继承的对象的constructer
        }
        say(){
            alert(this.name + ' say')
        }
    }
    
    const dog = new Dog('哈士奇')
    dog.say()
    dog.eat()
    

五、总结

  1. class在语法上更贴近面向对象的写法。
  2. class实现继承更加易读易理解。

前端安全类

  • CSRF
    CSRF(Cross-site request forgery):跨站请求伪造。
    CSRF:又称XSRF,冒充用户发起请求(在用户不知情的情况下),完成一些违背用户意愿的请求(如恶意发帖,删帖,改密码,发邮件等)。

  • XSS

页面性能

提升页面性能方法:

  1. 资源压缩,减少HTTP请求

  2. 非核心代码异步加载 → 异步加载的方式 → 异步加载的区别

  3. 利用浏览器缓存 → 缓存的分类 → 缓存的原理

  4. 使用CDN

  5. 预解析DNS

    <meta http-equiv="x-dns-prefetch-control"
    content="on" />
    <link rel="dns-prefetch"
    href="http://bdimg.share.baidu.com" />
    
  • 异步加载的方式:

    1. 动态脚步加载,创建动态js标签
    2. defer → 是在HTML解析完之后才会执行,如果多个,按照加载的顺序依次执行
    3. async → 是在加载完成之后立即执行,如果是多个,执行顺序和加载顺序无关
  • 缓存的分类: http 请求头

    1. 强缓存
      Expires(绝对时间): Thu, 21 Jan 2017 18:25:02 GMT
      Cache-Control: max-age=360(秒)
    2. 协商缓存
      Last-Modified if-Modified-Since
      Etag If-None-Match

错误监控类

  • 即时运行错误:代码错误

    1. try...catch
    2. window.onerror
  • 资源加载错误(不会冒泡,但可捕获)

    1. object.onerror
    2. performance.getEntries()
    3. Error 事件捕获
    window.addEventListener('error',function(e){
      (new Image()).src="http://xxxxx上传反馈错误的信息地址";
    },true);
    
  • 上报错误基本原理

    1. ajax上报
    2. Image对象上报 ⭐
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容