《你不知道的JavaScript》学习佛系梳理

根据《你不知道的JavaScript》以及随缘想到的问题整理成册
Author:付紫琴

如何理解执行上下文?

执行上下文需要感性理解,上下文即承上启下,即当前对象在程序中的一个执行环境;分全局上下文,函数上下文,和特殊的eval上下文,
上下文中包含了变量,作用域和this指针,每一段js执行都有产生自己的上下文.

如何理解作用域链?

在js执行的上下文中包含作用域链,作用域则是根据变量名称查找变量定义的一套规则,当存在块嵌套和函数嵌套时,会一层一层向父级查找,直到查找到全局作用域才会停止.

在编译原理的层面上说:即在使用的地方向上查找该变量在哪里声明的.
其中特殊的两个机制:eval()和with()可以修改变量的词法作用域
前者可以用字符串的形式声明了已经存在的变量,
后者则是通过对对象在with的作用域范围内做左查询来修改变量的值,
但是一旦个值不存在,则会在with的同级作用域内声明一个变量,变量泄漏到全局作用域了
使用这两个都会使引擎编译变慢

块作用域和函数作用域

声明在一个函数内部的变量或函数会
在所处的作用域中“隐藏”起来,这是有意为之的良好软件的设计原则;
但函数不是唯一的作用域单元。块作用域指的是变量和函数不仅可以属于所处的作用域,
也可以属于某个代码块;

有趣的是:块作用域非常有用的原因和闭包及回收内存垃圾的回收机制相关,在块作用域中占大量内存的数据结构会被回收

判断一个运行中函数的 this 绑定

需要找到这个函数的直接调用位置;我们关注这个函数在哪里调用,并非在哪里声明,this的指向只与第一层和最后一层有关;
可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。
  1. 有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑
    定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null),以保护全局对象。
  2. 箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这
    其实和 ES6 之前代码中的 self = this 机制一样。

var let const的区别

ES5没有块级作用域的概念,ES6新增块级作用域;

  1. var定义的变量,没有块的概念,可以跨块访问,
  2. let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
  3. const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
  4. 不能跨函数访问。var存在变量提升,可以在定义前使用,值为undefined,let和const不会
console.log(fuziqin)
> VM105:1 Uncaught ReferenceError: fuziqin is not defined at <anonymous>:1:13 (anonymous) @ VM105:1

console.log(fuziqin1);
var fuziqin1=1
> undefined
  1. var是顶层变量,在全局声明的变量,var声明的变量会同时是顶层对象的属性,但是let与const不是。(浏览器内,顶层对象是windw,node是global,采用this在两个环境内都可)
var var1 = 1;
console.log(this.var1);//var1

let let1 = 1;
console.log(this.let1) // undefined

let const1 = 1;
console.log(this.const1) // undefined

什么是闭包

当函数可以记住并访问当前作用域时,就产生了闭包,即使函数在当前作用域外执行;
因为JavaScript的垃圾自动回收机制,在块作用域外,不再使用的内存空间将会被回收,但是闭包却可以阻止该事件的发生,该内部函数作用域一直存活,直到在作用域外被调用声明时的词法作用域

bar() 依然持有对该作用域的引用,而这个引用就叫作闭包,在几微秒之后变量 baz 被实际调用(调用内部函数 bar)这个函数在定义时的词法作用域以外的地方被调用。

function foo() {
 var a = 2;
 function bar() { 
 // 拥有涵盖 foo() 内部作用域的闭包,使得该作用域能够一直存活,以供 bar() 在之后任何时间进行引用
 console.log( a );
 }
 return bar;
}
var baz = foo();
baz(); // 2
延伸 模块模式(模块暴露)
  1. 必须有外部封闭函数,该函数至少呗调用一次(每次调用会产生一个模块实例)
  2. 封闭函数至少返回一个内部函数,这样才能再封闭函数内形成闭包,并可以访问和修改私有内容
    特点:为函数定义引入包装函数,并保证它的返回值和模块的 API 保持一致
var MyModules = (function Manager() {
    var modules = {};
    function define(name, deps, impl) {
         for (var i=0; i<deps.length; i++) {
            deps[i] = modules[deps[i]];
        }
    modules[name] = impl.apply( impl, deps );
    //modules.name指向函数并向里面传值
    }
    function get(name) {
         return modules[name];
    }
    return {
             define: define,
             get: get
         };
})();

MyModules.define( "bar", [], function() {
    function hello(who) {
        return "Let me introduce: " + who;
    }
    return {
        hello: hello
    };
} );

MyModules.define( "foo", ["bar"], function(bar) {
    var hungry = "hippo";
    function awesome() {
        console.log( bar.hello( hungry ).toUpperCase() );
    }
     return {
        awesome: awesome
    };
} );

var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );

console.log(
    bar.hello( "hippo" )
); // Let me introduce: hippo

foo.awesome(); // LET ME INTRODUCE: HIPPO
 

call/apply/bind作用和区别

三个方法都可以改变this指向(是this的硬绑定),第一个参数都是this的指向对象

  1. bind返回的一个function,可以执行,也可以new实例
  2. call和bind接收参数都是逗号分隔的单个的对象,而apply接收的是数组参数,并在内部实现展开传入函数
/** 手写call/apply/bind **/

    <script>
        Function.prototype.myCall = function(context) {
            if(typeof this !== 'function') {
                throw new TypeError('error')
            }
            context = context || window;
            // 获取函数
            context.fn = this;
            // 获取参数
            const args = [...arguments].slice(1)
            const result = context.fn(...args)
            delete context.fn
            return result
        }
        Function.prototype.myApply = function(context) {
            if(typeof this !== 'function') {
                throw new TypeError('error')
            }
            context = context || window;
            // 获取函数
            context.fn = this;
            // 获取参数
            let result
            if(arguments[1]) {
                result = context.fn(...arguments[1])
            } else {
                result = context.fn()
            }
            return result
        }
        Function.prototype.myBind = function(context) {
            if(typeof this !== 'function') {
                throw new TypeError('error')
            }
            context = context || window;
            const _self = this;
            const args = [...arguments].slice(1);
            return function F(){
                console.log(new.target,111111111111)
                if(this instanceof F) {
                    return new _self(...args,...arguments)
                } else {
                    _self.apply(context,args.concat(...arguments))
                }
            }
        }
        function a(...b){console.log(1,b)}
        let b = a.myBind({a:1},[1,2,3])
        new b()
    </script> 
延伸:函数柯里化

维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

  1. 参数复用
  2. 提前确认
  3. 延迟运行
    封装方式参考bind,差不多

缺点

  • 存取arguments对象通常要比存取命名参数要慢一点
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的
  • 使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
  • 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上

什么是深/浅拷贝,有哪些实现方式

浅拷贝是copy对象的引用,改变引用地址的值,则该复制对象也改变
深拷贝是彻底的复制该对象

浅拷贝的实现方式
  1. 直接赋值
  2. Object.assign() // 一层是深拷贝,多层是浅拷贝
  3. ES6扩展运算符 // let newObj={ …obj }
  4. jQuery.extend方法 let newObj = jQuery.extend({}, obj);
  5. 类数组对象拷贝 Array.from(array1) / Array.concat() / Array.slice()等等
深拷贝的实现方式
  1. var newObj = JSON.parse(JSON.stringify(obj));
  2. jQuery.extend方法 var newObj = jQuery.extend(true, {}, obj);
  3. Object.assign() //一层是深拷贝,多层是浅拷贝
  4. jQuery.extend第一个参数指示是否深度合并
    // $.extend( [deep ], target, object1 [, objectN ] )
  5. 函数递归调用
var deepCopy = function(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
	if (obj.hasOwnProperty(key)) {
	    newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}
var obj = { a:1, arr: [1,2] };
var newObj = deepCopy(obj);
obj.arr[0] = 10;
console.log(newObj.arr[0]); // 1

如何理解原型链?

提到原型链就不得不理解prototype,prototype就是对象中的一种内部链接,他会引用其他对象;如果在对象上没有找到需要的属性或者方法引用,引擎就
会继续在 [[Prototype]] 关联的对象上进行查找。这一点和作用域很像.

每个函数都有prototype属性,指向一个对象,即函数的原型;

每个函数都有实例对象__proto__属性,而这个实例对象__proto__指向函数的prototype,

当我们查找一个实例的属性或者方法时,会先去他的构造函数里面查找,如果没有就去__proto__原型(1)中查找,即父级函数的prototype,而这个父级函数中依然没有会一直向上查找,object.prototype.__proto__为null,即原型链的终点.

// child.proto == parent.prototype

(1)对__proto__进行延展,笨蛋Proto其实也不是一个属性,
是和constructor一样内置在prototype中,
__proto__更像是构造调用了获取一个对象的 [[Prototype]] 链方法 Object.getPrototypeOf( this)获取了prototype,如下所示
Object.defineProperty( Object.prototype, "__proto__", { 
 get: function() {
 return Object.getPrototypeOf( this ); 
 },
 set: function(o) {
 // ES6 中的 setPrototypeOf(..)
 Object.setPrototypeOf( this, o );
 return o;
 } 
} );

继承有哪些方法

js的继承并不会复制对象属性,而是以委托的形式将父和子中间产生关联,也就是原型链

1. 原型链继承

特点:

  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点:

  • 无法实现多继承
  • 来自原型对象的所有属性被所有实例共享
  • 创建子类实例时,无法向父类构造函数传参
  • 要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中
        //父类型
       function Person(name, age) {
           this.name = name,
           this.age = age,
           this.play = [1, 2, 3]
           this.setName = function () { }
       }
       Person.prototype.setAge = function () { }
        //子类型
       function Student(price) {
           this.price = price
           this.setScore = function () { }
       }
       Student.prototype = new Person() 
       // 子类型的原型为父类型的一个实例对象
       var s1 = new Student(15000)
       var s2 = new Student(14000)
       console.log(s1,s2)
2. 借用构造函数继承

特点:

  • 解决了原型链继承中子类实例共享父类引用属性的问题
  • 创建子类实例时,可以向父类传递参数
  • 可以实现多继承(call多个父类对象)

缺点:

  • 实例并不是父类的实例,只是子类的实例
  • 只能继承父类的实例属性和方法,不能继承原型属性和方法
  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setName = function () {}
  }
  Person.prototype.setAge = function () {}
  function Student(name, age, price) {
  // 在子类型构造函数中通用call()调用父类型构造函数
    Person.call(this, name, age)  // 相当于: this.Person(name, age)
    /*this.name = name
    this.age = age*/
    this.price = price
  }
  var s1 = new Student('Tom', 20, 15000)
3.组合继承(组合原型链继承和借用构造函数继承)(常用)

优点:

  • 可以继承实例属性/方法,也可以继承原型属性/方法
  • 不存在引用属性共享问题
  • 可传参
  • 函数可复用

缺点:

  • 调用了两次父类构造函数,生成了两份实例
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。
        function Person(name, age) {
            this.name = name,
            this.age = age,
            this.setAge = function () { }
        }
        Person.prototype.setAge = function () {
            console.log("111")
        }
        function Student(name, age, price) {
            Person.call(this,name,age)
            this.price = price
            this.setScore = function () { }
        }
        Student.prototype = new Person()
        Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
        Student.prototype.sayHello = function () { }
        var s1 = new Student('Tom', 20, 15000)
        var s2 = new Student('Jack', 22, 14000)
        console.log(s1)
        console.log(s1.constructor) //Student
        console.log(p1.constructor) //Person
4. 原型式继承(实例继承)

优点:

  • 不限制调用方式

简单,易实现

  • 缺点:不能多次继承
function Wonman(name){
  let instance = new People();
  instance.name = name || 'wangxiaoxia';
  return instance;
}
let wonmanObj = new Wonman();
5. 寄生式继承

首先我们复制一份 Vehicle 父类(对象)的定义,
然后混入子类(对象)的定义(如果需要的话保留到父类的特殊引用),然后用这个复合对象构建实例。

// “传统的 JavaScript 类”Vehicle
function Vehicle() { 
    this.engines = 1;
}
Vehicle.prototype.ignition = function() {
    console.log( "Turning on my engine." );
};
Vehicle.prototype.drive = function() { 
    this.ignition();
    console.log( "Steering and moving forward!" );
};
// “寄生类” Car
function Car() {
     // 首先,car 是一个 Vehicle
     var car = new Vehicle();
     // 接着我们对 car 进行定制
     car.wheels = 4;
     // 保存到 Vehicle::drive() 的特殊引用
     var vehDrive = car.drive;
     // 重写 Vehicle::drive()
     car.drive = function() { 
     vehDrive.call( this ); 
     console.log(
     "Rolling on all " + this.wheels + " wheels!" 
 );
 return car; 
}
var myCar = new Car();
myCar.drive();
// 发动引擎。
// 手握方向盘!
// 全速前进! 
6. 寄生组合式继承(常用)
//父类
function People(name,age){
  this.name = name || 'wangxiao'
  this.age = age || 27
}
//父类方法
People.prototype.eat = function(){
  return this.name + this.age + 'eat sleep'
}
//子类
function Woman(name,age){
  //继承父类属性
  People.call(this,name,age)
}
//继承父类方法
(function(){
  // 创建空类
  let Super = function(){};
  Super.prototype = People.prototype;
  //父类的实例作为子类的原型
  Woman.prototype = new Super();
})();
//修复构造函数指向问题
Woman.prototype.constructor = Woman;
let womanObj = new Woman();
7.es6 extend super关键字继承
//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
  constructor(name='wang',age='27'){
    this.name = name;
    this.age = age;
  }
  eat(){
    console.log(`${this.name} ${this.age} eat food`)
  }
}
//继承父类
class Woman extends People{ 
   constructor(name = 'ren',age = '27'){ 
     //继承父类属性
     super(name, age); 
   } 
    eat(){ 
     //继承父类方法
      super.eat() 
    } 
} 
let wonmanObj=new Woman('xiaoxiami'); 
wonmanObj.eat();

如何准确判断一个对象是数组

  1. Array.prototype.isPrototypeOf(obj);
  2. obj instanceof Array
  3. Object.prototype.toString.call(obj)
  4. Array.isArray()

数组有哪些常用方法

split()将字符串转化为数组
join() 将数组转化为字符串
栈方法 push() pop()进栈和出栈
队列方法 shift()从队列的前端移除 unshift()在前端增加参数
reverse()反转数组
sort()排序
concat()连接两个数组
slice() 从索引处截取到数组末尾
splice() 往数组中插入或者删除
ES6新增的
Array.of()新增元素-Array(2)是改变size
Array.from() 提供了第二个参数可以映射到返回值
Array.copyWith() fill()  find()  findIndex()  entries()  value() keys()等


数组的去重
1. ES6新增set的数据结构是数据无重复的
Array.from(new Set(arr))
2. 双重循环 冒泡遍历相等就splice
3. indexOf 新建一个空数组,遍历旧数组 if(~arr.indexof(arr[i])) arr[i]push进空数组
4. sort()排序,相邻比较
5. 利用对象属性不重复
6. 遍历判断tempList.includes()
7. filter返回数组,object.hasOwnProperty(typeof item+item)
8. filter返回arr.indexof==index indexof返回第一个元素出现的索引
9. sort 在递归
10. 利用map数据结构,new arr
11. reduce+includes arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[])
12. [...new set(arr)]
数组如何排序

多看!!硬着头皮看!!忘了再看就记住了!!!十大经典排序算法

https://www.cnblogs.com/onepixel/articles/7674659.html

1. sort(function(x,y){return x-y})
2. 待完善
谈一下for…of
  1. for of ES6新增;可以遍历数组对象的每个元素,for of会向对象请求一个迭代器,调用他的next()来遍历所有返回值
    for of 可以应用解构,使用对象中的key value,结合map等多种灵活用法;
  2. for in 是遍历出列表的索引 ;会遍历对象的所有可枚举属性;;;

谈一下EventLoop

事件循环 Event Loop
  1. 同步的进入主线程,异步的进入event table执行注册函数
  2. 当异步任务完成会进入event queue
  3. 主线程的任务完成会从event queue中将任务推进主线程执行
  4. js引擎monitoring process进程会不断在主线程中检查是否为空,一旦为空就会去等待队列中调用等待被调用的函数.
  5. 上述过程会不断重复.即event loop

例如promise就是基于js的这一时间循环机制解决了异步执行函数的信任问题:比如调用过早,调用过晚,甚至不调用,调用次数过少,参数丢失,未能处理错误和异常这些问题

同步任务 异步任务setTimeout setinterval 宏任务 微任务Promise process.nextTick
JavaScript没有多线程,都是单线程模拟出来的,通过事件循环event loop实现异步
例子:
setTimeout(()=>{
    task()
},3000)
console.log(1);
sleep(1000000000000)
setTimeout进入eventtable注册task()
定时进入eventtable,3秒后task进入等待队列 ,同时主线程console=>睡醒了进入等待队列拿task
除开广义的同步和异步还有宏任务和微任务这个概念
script,setTimeout,setinterval这些属于宏任务
promise .then Process.nextNick属于微任务
**理解这个问题的重点是:同步任务和微任务是交替执行的;**
主任务执行完了之后就去微任务队列里拿等待队列,微任务执行完了之后就执行宏任务...
  

手写promise

https://segmentfault.com/a/1190000020505870
给我读!!!认真读!!!
里面要传入一个resolve方法,和reject方法,其中resolve会对传入的事件进行封装成异步的行为;
怎么使用=>解决了什么问题=>api then catch all race等
=>大致实现=>也存在bug

正则表达式

  1. 普通字符
    /[ABC]/ 匹配其中的字符
    /[^ABC]/ 匹配除了其中的字符
    /[\s\S]/ \s 匹配空白符包括换行;\S非空白符不包括换行
  2. 非打印字符 转义字符
    \cx \f \n \r等等
  3. 特殊字符
    $ 匹配字符串结尾位置
    () 子表达式的开始结束
    • *前面出现0到多次
    • +前面至少一次
      ? 前面的子表达式0到一次
      | 两项中选一个
  4. 限定符
      • 贪婪,尽可能多的匹配文字(?非贪婪);
        、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
        例如:/<.
        ?>/ /<\w+?>/
        {n} 确定的n次
        {n,} 至少n次
        {n,m} 至少n至多m

ES6新增特性

待完善:https://www.runoob.com/regexp/regexp-syntax.html


# ES6更新了哪些东西

1. 语法上新增了块作用域的概念,明确了let是块作用范围的变量;更新了很多语法糖:例如spread/rest表达式,解构赋值,模板字面量实现字符串和变量混合编写,Symbol对象
2. 代码组织 迭代器,生成器
3. 异步流控制promise
4. 集合set map这样的数据结构
5. 新增的API  


# webpack

##### 为什么要使用webpack
webpack将文件看成一个一个的模块,传统的项目加载多个js需要多次进行http请求,现使用webpack就只有一个入口一个出口,减少了请求压力,增加了打包和请求效率

1.默认打包js资源
2.html插件将以html为模板生成一个html供浏览器使用
3.img 依赖fileloader urlloader(还可以引入其他资源,对fileloader进行封装)
4.css styleloader cssloader
5.babel将高级语言转化成低版本浏览器可以识别的语言,polyfill将新更新的语法以补丁的形式打包出来供浏览器使用
6.等等
还没看完待完善


## transform transition动画和animation有什么区别
CSS变形(transform)呈现的仅仅是一个“结果”,而CSS过渡(transition)呈现的是一个“过程”。
(1)对于transition属性来说,它只能将元素的某一个属性从一个属性值过渡到另一个属性值。
(2)对于animation属性来说,它可以将元素的某一个属性从第1个属性值过渡到第2个属性值,然后还可以继续过渡到第3个属性值,以此类推。
从上面我们可以清楚地知道:transition属性(即CSS3过渡)只能实现一次性的动画效果,而animation属性(即CSS3动画)可以实现连续性的动画效果。

## DOM节点创建和修改有哪些常用API
操作元素节点

创建节点:document.createElement
插入节点:appendChild insertBefore
删除节点:remove
复制节点:cloneNode
替换节点:replaceChild

操作属性节点

div.title
getAttribute

操作文本节点

innerHtml


# CSS清除浮动有哪些方法
1
2
3
.outer{border: 1px solid #ccc;background: #fc9;color: #fff; margin: 50px auto;padding: 50px;} .div1{width: 80px;height: 80px;background: red;float: left;} .div2{width: 80px;height: 80px;background: blue;float: left;} .div3{width: 80px;height: 80px;background: sienna;float: left;} ``` 1. 添加新元素应用clear:both,还可以使用空标签清除浮动 ``` .clear{clear:both; height: 0; line-height: 0; font-size: 0}

``` 2. 父级div定义 overflow: auto;使用overflow属性来清除浮动有一点需要注意,overflow属性共有三个属性值:hidden,auto,visible。我们可以使用hiddent和auto值来清除浮动,但切记不能使用visible值,如果使用这个值将无法达到清除浮动效果,其他两个值都可以。 ``` .over-flow{ overflow: auto; zoom: 1; //zoom: 1; 是在处理兼容性问题ie6 } ``` 3. after 方法:(注意:作用于浮动元素的父亲) ``` .outer {zoom:1;} /*==for IE6/7 Maxthon2==*/ .outer:after {clear:both;content:'.';display:block;width: 0;height: 0;visibility:hidden;} /*==for FF/chrome/opera/IE8==*/ ``` # CSS选择器优先级 总结排序:!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性

谈一下flex布局

弹性盒子模型:

定义display:flex让元素变成弹性盒子,子元素宽度之和<=父元素宽度
flex-grow:按比例放大;争取父元素宽度的比例,子元素一般不设置固定宽度以免影响比例分配
flex-shrink:按比例缩小
flex-basis:等价于width,用于flex盒子下的子元素,便于语义化
flex-direction:子元素的排列方向,取值:row/row-reverse/column/column-reverse
flex-wrap:nowrap/wrap/wrap-reverse
order:子元素的排列顺序
justify-content:flex-start/center/flex-end/space-between/space-around定义子元素在横轴上的对齐方式
align-items:flex-start/center/flex-end/baseline/strecth纵轴上的对齐方式
复合属性:flex: grow shrink basis;
复合属性:flex-flow: direction wrap;

CSS实现三列布局(左右固定宽度,中间自适应)

实现的方式有太多种了
1.脱离文档流,浮动
2.使用定位
3.flex布局
4.calc()计算
5.column瀑布用法
等等等

谈一下盒模型

盒模型由里向外是content padding border margin,

有两种:
标准模型:width = content+padding+border
IE模型:width = content
除此之外还有css3新增的flex弹性盒模型和column多列盒模型

box-sizing:content-box//标准  |  border-box//IE

GET和POST有什么区别

get和post都是http协议下的两种请求方式
get参数在url,会被浏览器记住在历史记录里,post不会
post参数在requestBody,get比post更不安全
post会发送两个tcp请求包,第一次传送head,第二次传送data;firefox只发送一次,
但是这两次的请求请求速度差不了多少,可以忽略不计
所以基于安全性考虑,get用于请求数据,post用于携带多重信息发送信息

浏览器解析渲染页面过程

构建dom树 html css,加载js,构建render树进行渲染,布局render树,绘制render树,树节点的重绘和重排

H5自适应方案

rem缩放比,upx vw vm这样的单位,可以做到一定的适配
另也有插件lib-flexible、postcss-plugin-px2rem进行rem适配

websocket心跳

vue源码理解

在这里插入图片描述

主要实现mvvm双向绑定,vue里面会定义两个主要的对象

  1. observer
  2. compiler
  3. 首先实现observer()将data里面的数据,采用数据劫持结合发布订阅模式,通过object.defineProperty来劫持各个属性的get和set
  4. get就做了初始化数据的操作
  5. 实现指令解析器compile对页面的指令进行解析,并new了watcher对象进行订阅数据的变化
  6. 一旦数据发生变化,就会走observer的set方法,notify通知更新,compile中对指令new的watcher订阅到了消息就会去dep里面找到对应的watcher进行update操作;

到这里就实现了数据更新视图

  1. 对指令对应的数据进行监听,一旦发生变化就更新数据;

这一步就实现了视图驱动数据

所以watcher就建立了observer和compile的桥梁,做了几件事:

  1. 在自身实例化的时候往dep里面添加自己
  2. 有一个update数据的方法
  3. 在observer数据变化时dep.notify被调用的时候触发compile中绑定的回调

MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。


观察者和发布订阅者区别

谈一下防抖和节流

谈一下常用设计模式,并选择一个进行场景分析

前端常见攻击方式
前端有哪些跨域方案
前端网站常规优化方案

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值