seho 发布的文章

今天是周天,本来周五晚上就想写这一篇总结,但是每周分享的题总结还没结束,只能结束之后新写这一个文章。
其实,昨天在逛github的时候给我推荐了几个不错的仓库博客,类似于《一周攻克一个前端难点》,我也会有时间去学习,并且把学习到的干货分享到博客上来。

关于这边《ECMAScript 6 入门》,我没有买这个书而是看阮一峰老师的电子开源书籍,周五看了第一章关于let和const,然后加深了我对于作用域,死区的理解,然后在书中也有非常多的demo例子,这些例子在笔试中还是高频率的出现的,一些前端开发者在面试(入行不深的程序员)选择google出来的答案,背下来....

但是这非常不实用,因为你不了解这种题如何做,在工作中如何避免bug。
在新专栏的开始之前,我想多说几句,和大家分享一下我的学习方法,我也是入行不深,仅仅只有半年多而已,但是写代码即将2年多,
我学习前端的方法也非常简单,项目实战(有利于能接触到更多的场景)-》刷题(渣渣程序员只能从语言角度上刷题,涉及不到算法数据结构的题)-》笔记和博客(每天总结笔记,每一段时间总结博客)要养成良好的习惯,不管多忙,在地铁和公交车上一定要把笔记写完,随手复习,工作中可以翻阅;

正题开始............

es6是前端开发者必须掌握的一个知识点,不管是培训结构还是某些大学的前端课程,传授的es6都相对非常浅,没有去认真的一篇一篇去消化巩固,作者也是这样的,所以现在开始我们将一起一步一步精读阮一峰老师的这本非常棒的书籍,把es6完全消化,能够让你在工作中面试中杀出一条血路。

u=3907368649,2159858588&fm=26&gp=0.jpg

首先我们来快速了解一下let;

Let

我们尝试写这样一段代码:


{
    let a = 1;
    var b = 10;
}

console.log(a); // Uncaught ReferenceError: a is not defined
console.log(b); // 10

这个例子说明了let只会在声明它的代码块中生效,而var则相反

我们一起再看一个例子:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1](); // 10

这个循环中的i是一个全局变量,每一次循环全局的变量i都会发生改变,所以尽管它循环100次,数组中console.log中的i永远指向全局变量即100,最后一次循环的赋值;

但是如果把上面的var换成let就不一样,let只在自己的块中存在,所以每次循环都会重新let一次i变量,所以每次输出的console.log中的i指的是当前循环到的i

但是你肯定会说:

既然是重新let一次i,那么每次它应该都是0呀,为什么还能记忆上次的i值呢?这不科学...

那是因为js引擎会记住上一次i的值,为下次循环做运算,这一部分是js帮助开发者做好的。

js的for循环还有一个特别之处:设置循环变量的是一个父作用域,而循环体是一个子作用域

for (let i = 0; i < 10; i++) {
    let i = '123';
    console.log(i) // 123
}

正确运行,并且成功赋值打印新的值;

Let - 关于变量提升

js中的var是存在变量提升的,什么是变量提升呢?

console.log(i); // undefined
var i = "I love you"

在变量未被附值的时候,js解析代码会变成下面这个样子:

var i;
console.log(i);
i = "I love you"

但是let并非这样,它没有所谓的变量提升,只要在声明前使用,则会报错;


console.log(a); // Uncaught ReferenceError: a is not defined
let a = "yes";

所以var会让变量提前声明,而let不会提前声明,如果声明前用到了它,就会报错Uncaught ReferenceError;

Let - 暂时性死区

文章的开头我们降到了死区这个概念,之前呀,我也不太懂这个死区,以为是一个很难的概念,没想到和let有关系,非常简单;

var a = "123";
{
    a = "125";
    let a = "456";
}

ES6明确规定,let和const声明了一个变量,那么会和当前作用域块进行绑定,如果在声明前使用到了变量,那么就会报错;
这在代码中我们称之为 "暂时性死区"

那么如果我们不向变量赋值会不会报错呢? 事实证明typeof也不是一个正确的操作


typeof x; // Uncaught ReferenceError
let x;

如果我们检测一个不存在的变量

typeof notfound; // undefined

所以说这也意味着我们编写代码的时候要在声明之后使用它;

在阮一峰老师的文章中也提到了不常见的死区的案例:

function test(x = y, y = 2){

}

test();

x的默认值是还未声明的y,所以会报错;

var x = x;  // 不报错
let x = x; // 报错

其实上面这个代码,之前就解释过了,var出来的变量即已对变量进行初始化,所以等号右边的x就是undefined
而let出来的变量没有变量提升,然后又在变量x声明之前使用到了x,这显然是错误的,因为之前使用的就是死区;

因此死区的触发有很多,检测,赋值,使用, 等等等;

Let - 不允许同时声明在同作用域中

{
    var a = 1;
    let a = 1;
}

或者

{
    let a = 1;
    let a = 1;
}

又或者

funtion test(arg){
    let arg = "123";
}

这些都会报错,但是在不同作用域中是可以的

funtion test1(arg){
    {
        let arg = "12";
    }
}

it is ok!!

关于块级作用域

面试中经常会被问到作用域, 全局作用域,函数作用域,在es6中新的概念就是块级作用域,之前面试过其他的前端开发者谈到块级作用域解决了什么问题,和let,const的关系,他们也是没有一个很准确的答案;

这边用阮一峰老师的2个demo例子:


var temp = new Date();
function test(){
    console.log(temp);
    if(true){
        var temp = "hello"
    }
}

test(); // undefined

上面这个例子之所以输入未定义,是var变量会造成变量提升,即var temp; 内层的temp替换掉了new Date的那个变量,提升到了函数作用域的顶级;

还有一种场景就是,我们在用循环的时候,使用var,导致循环结束var的变量依然存在,造成变量泄露;

事实上,let和const就是块级作用域,因为我们知道在前面的内容讲到let只能在当前作用域下使用,和其他的作用域不相干,这就是形成了块级作用域;

let a = 1;
if(true){
    let a = "1111"
}
console.log(a); // 1

ES6允许我们块级作用域嵌套

{{{{{
    console.log(1)
}}}}}

即内层的可以读取到外层同名变量,外层不能读取内层的同名变量;

块级作用域和函数声明

ES5规定,块级作用域里面不能写函数声明语句,但是浏览器为了兼容以前的旧代码,没有遵守这个规定;
但是ES6明确规定,块级作用域下可以写函数声明,但是函数声明类似于let,在作用域之外不能使用;

看一下下面这个demo:

function f() { console.log('I am outside!'); }
(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());

如果是在ES5中写这个函数,那么最终会输出 "I am inside!" ,因为会变量提升提前到函数顶部,所以会输出inside
但是如果在ES6中声明,理论来讲会输出outside,因为es6规定了函数声明相当于let,只在自己作用域生效,不会影响其他作用域,也没有变量提升,但是如果在ES6中就会输出undefined;

为什么呢?说好的规定呢?说好的let呢?

微信截图_20191027154441.png

原来浏览器又有自己实现的一套方法,输出undefined的原因还是被提升了

var f = undefined;

所以我们在作用域中写函数,不要用函数声明,要使用函数表达式,这样最稳妥...

const

ES6新增的常量,我喜欢叫它常量,因为它不可改变;

const a = 1;
a = 12; // TypeError: Assignment to constant variable.

且不能这样写

const a;

不能像var和let一样先定义再赋值;

const和let一样,只能在声明的块级作用域有效
const和let一样,也有死区
const和let一样,也不能重复声明

const的本质其实并不是const的变量值不能被改变,而是指针(地址)是固定的;

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
---------------《ECMAScript 6 入门》

所以我们可以改变const的数据结构

const props = {};
props.name = "1";
props.age = "12";
props = "12"; // 报错



结束咯,喜欢这篇总结,就收藏起来吧~~
原文阮一峰老师地址: http://es6.ruanyifeng.com/#docs/let

写在前面: 本文章的答案可能不是很完整,因为毕竟是面试/笔试回答,如果单领一个知识都能凑一篇文章,所以回答仅供参考,大家一起学习~~

BOM和DOM的区别

首先BOM > DOM,前者是后者的集合,BOM是浏览器对象,宿主是浏览器存在的对象,浏览器提供了很多自己的api供js调用,而dom是文档结构对象,提供了文档相关的API,dom和bom在node中没有这个概念,浏览器下才有

如何让字体变得更细更清晰

选用字体更清晰的font-family / ,图片,或者使用谷歌属性抗锯齿font-smoothing,但是这个兼容性不好

说说你对HTML元素的显示优先级的理解

frameset元素层级最高;
有窗口的比没窗口的高; video,audio等
表单元素比非表单元素高;input
z-index能提升同等级元素的高度,不能跨等级;

说下line-height5种赋值方式有何区别?

  • 默认行高
  • 设置数字,是父元素的font-size倍数
  • 固定行间距,固定的值
  • % 父元素的font-size百分比
  • 继承inherit

造成内存泄漏的操作有哪些?

递归,闭包,定时器没clear等

html和html5有什么区别呢?

标准的区别: HTML是基于SGML的,HTML5不是,SGML是标记语言的集合,xml和html都是基于它的标准来的,所以html要写DTD规范,是因为SGML的要求需要告诉浏览器以哪种标准来渲染;
语义化的区别,在机器和人类阅读来讲,HTML5的新增语义化标签会更友好
SEO的区别:SEO支持更好
功能区别:CANVAS和音频媒体的支持,使得H5不仅仅可以做网页,可以做游戏甚至是可穿戴设备
全面拥抱移动端 nice

用CSS绘制一个三角形

原理: 利用了border切口处是三角形的这个特性,宽高为0,将二边隐藏透明色,一边扩大border的宽度即可

说说你对this的理解

这边简单提一下,因为this这个概念还是要说一个比较大的篇幅的
this指的是当前运行时的上下文,this是会发生改变的,比如apply,call,bind
非严格模式下,全局this是window
函数this是谁调用了它,谁就是this

Standards模式和Quirks模式有什么区别?

浏览器的执行模式,一个是标准模式一个是怪异模式
标准模式是w3c规定的标准模式
怪异模式是为了兼容各个浏览器以前不标准的时候提出的解决方案
标准模式下的宽度,是内容宽度
怪异模式下的是border+padding即宽度
可以使用border-box: border-box来使用怪异模式。content-box是标准模式。

浏览器是怎样判断元素是否和某个CSS选择器匹配?

如果我没记错的话,有一个集合的概念,浏览器解析文档的时候,会把最后一个索引的元素集合到一个集合中,这个索引指的是

.class .red div {}

会把最后一个索引(div)的所有元素放在一个集合中,然后选择器从右到左进行查找,根据集合中的元素从下往上去查找,如果通过匹配父类不成功的元素会从这个集合中消失,直到整个集合留下最后一个成功的,就进行渲染,当然不同的浏览器还会有其他的优化,这一块也不是特别了解。

如何用div写出类似textArea的效果?

主要利用

contentedittable="true"这个属性进行div可编辑,可以利用以下css进行调整:

resize: both;width: 300px;
height: 200px;
padding: 5px;
border: solid 1px #ccc;
resize: both;
overflow:auto;

使用flex实现三栏布局

太过于简单面试题,讲一个思路就可以了,首先最中间肯定是写最前面的,因为优先渲染;
利用flex布局,左右两边flex: 0 0 auto; 中间: flex: 1 1 auto; 利用order排序即可

请你解释一个为什么10.toFixed(10)会报错?

浏览器在解析js中出现了扰乱,小数点在js中也有调用方法的作用,那么10是整数,js以为10后面跟的小数点是小数,没想到是直接调用了方法,让引擎执行代码中出现了冲突;

如果是0.1.toFixed不会报错;
解决办法:

10..toFixed(10);
(10).toFixed(10);

讲一下前端性能有哪些方面可以优化?

这个非常多,从很多方面来简单说一下;

雪碧图,websocket(不要使用轮询和长轮询),图片服务器,data属性,CDN,代码分割,减少dom操作次数,避免重排,懒加载,预加载

webpack方向:

treeshaking,preload,prefetch,异步加载模块(非常重要);

PWA: 离线存储,service-work

HTML与XHTML有什么区别?

XHTML中必须正确嵌套,必须小写,必须闭合标签

写出主流浏览器内核私有属性的css前缀

  • -webkit- 谷歌和sa
  • -0- 欧朋
  • -ms- ie
  • -moz- 火狐

关于前端安全

讲真不太了解,大概知道一点点的概念;

  • XSS跨站脚本攻击:

一般是sql文件或者js文件,对用户输入的东西不要信任,服务端返回的也不要信任,全部需要转义,要带上请求头和cookie等

  • csrf跨站请求伪造

这种防御系数很高,防御资料很少,一定程度来讲比XSS防范程度要高,把持久化的验证方式转换为瞬时验证方式,比如给form表单添加隐藏的信息等等

语义化标签有哪些?

header footer menu section article aside main nav等等

找到字符串中最长的单词,并返回它的长度

function test1(str){
    str = str.split(" ");
    return str.reduce((start, end) => {
        return start.length > end.length ? start : end;
    })
}

cookie和session的区别

cookie主要是客户端产生,体积很小只有4k,服务端也可以设置,可以设置时间等
session是服务端生成的,通常给客户端返回sessionid,前端存储在cookie中进行鉴权等操作;

说说你对eval的理解

一个小型的语言解释器,括号内填写js代码字符串,但是不宜与维护,而且不安全,非常不建议使用;
但是某些场景下,比如webpack中的soucemap就使用到了eval

你知道网页三剑客指的是什么吗?你有用过Dreamwear吗?

dw(不是那个手表,是dreamwear),firework,flash

title与h1、b与strong、i与em的区别分别是什么?

title是网页标题是在head中,h1是网页中的大标题加粗,类似于word中的h1,有很高的seo权重
b和strong都是加粗,但是strong会有警告的作用
i和em是html网页物理标记,告诉浏览器以什么格式渲染

写出你知道的CSS水平和垂直居中的方法

flex就不写了
定位和margin,百分之50减去自身高度宽度的一半
transform同理
不考虑兼容性可以使用calc函数

说说你对模块化理解

前端工程化的到来,尤其是node的兴起,模块化已经离不开我们,在日常的开发中,模块化有利于我们的代码维护,代码修正,代码复用等等,AMD,ES,COMMON,几个常用的模块方式已经非常流行,组件化的开发也得到了数以万计的项目的证实,确实是符合现代化工程(本人胡说一通)

只要答准amd,common,es等,这些历史,如何使用,项目中是如何分配的,讲区别就可以了;

html5都有哪些新的特性?移除了哪些元素?

新增了表单的type类型,对画布的支持,对存储的支持data-,对媒体的支持
移除了center,frame,frameset标签

怎么才能让图文不可复制?

-webit-user-select: none;

为什么会有跨域,解决办法是什么?

浏览器的同源策略,也是为了web应用的安全,不受信任的服务之间的信息传递
同源概念: 不同协议,不同端口,不同域名
主要解决方案有好几个
jsonp 只能get请求
cors 需要后端配合
proxy: 代理转发
禁止浏览器跨域安全策略

如何兼容websocket ?

websokect是一种协议,它是新特性,不兼容低版本的浏览器
有轮询,即在一定时间进行请求,服务端有新请求则返回,缺点:请求次数会非常多,并发压力
有长轮询,即服务端返回结果,再发起下一次请求,缺点: 对服务器的有并行压力
SSE也是可以,eventsouce对象,仅仅是知道,没用过

如何让英文单词的首字母大写?

text-transform: uppercase;

说说你对IIFE的理解

立即执行函数,为了形成闭包,其中的变量不会和全局变量进行污染
立即执行函数的几个必要条件:

  1. 必须是函数表达式 2. 声明之后必须()立即调用它
    一般我们见到的是这样的:

    (function(){}()) 或者 (function(){})()
    他们是等价的,同理它们也是一样的:
    !function(){}() 或者 ?funtcion(){}()
    前面的特殊符号只为了把函数声明转换为表达式

写重置css有什么用?

重置业务相关的css,利于还原设计图,进行多个浏览器的不同样式校对

window对象和document对象有什么区别?

这个很像第一题,它们是一个题

如何让元素固定在页面底部?有哪些比较好的实践?

position fixed boottom: 0;
还有粘性定位,不过兼容太差,不建议使用

说说video标签中预加载视频用到的属性是什么?

prderload="preload"

手写一个品字布局的方案

第一行居中
第二行简单来讲可以用flex,space-around
也可以用其他方式,非常简单

深拷贝实现

递归实现

function deepclone(obj){
let _obj = {};
for(let key in obj){
    if(obj[key] && typeof obj[key] === 'object'){
        _obj[key] = deepclone(obj[key]);
    }else{
        _obj[key] = obj[key]
    }
}
return _obj;
}

xml与html有什么区别?

XML和html都属于SGML标准中
XML要求必须闭合标签,而且主要是存储数据的,多个空格不会读成一个空格
html反之

你知道的等高布局有多少种?写出来

top和bottom是0
padding-bottom: 9999999999px; margin-top:-99999999999999px;使用浮动再 清除浮动,即等高
flex默认就是盛满父盒子

写出几种创建对象的方式,并说说他们的区别是什么?

字面量
let obj = {};
new一个构造
let obj = new Object();
Object创建
let obj = Object.create(); // 这个可以设置原型

页面中怎么嵌入Flash?有哪些方法?写出来

object标签,embed标签

说说你对媒体查询的理解

根据业务的不同,进行宽度高度等其他因素的调整,要用到媒体查询,目前最流行的就是css3的媒体查询,
在一定程度上可以满足所有的设备,实现一套代码多端运行的效果

写一个使2个整数交换的方法

let [a,b] = [b,a];

HTML5如何使用音频和视频?

video标签和audio标签

你是怎样抽离样式模块的?

样式模块分为几种,公用样式,业务样式,重置样式,通过css分离,将业务组件中的代码和其他组件的样式
进行分离互不干扰;可以高效利用css预编译工具中的函数等封装一些高频出现的样式

请说说你对事件冒泡机制的理解?

事件分为几个阶段,第一个是捕获阶段-》第二个是目标阶段-》第三个是冒泡阶段
冒泡是从内而外进行传递的,当点击子元素的事件,那么相应的父级元素的事件也会触发
冒泡可以使用event.stop*,一个方法,我忘记了。。。
但是一些业务场景来讲,比如列表,我们没必要去给每一列去绑定事件,可以在父级上定义事件,通过事件对象的排查,确定是第几个元素点击了click,那么这将是优化长列表的一种常见的手段

说说你对WEB标准和W3C的理解与认识?

web标准是web最正规的标准,以前的代码时js,css,html混在一起,为了模块化,web标准规范了这一点;
w3c是web标准之上进行调整强化的一套标准,比如标签名小写,正确嵌套,语义化等等

你知道全屏滚动的原理是什么吗?它用到了CSS的哪些属性?

全屏滚动的原理其实和轮播图很类似,只不过搭配了一些css,比如高度和宽度沾满了整个屏幕,overflow: hidden; 然后监听js的滑动事件,进行dom的切换

你对事件循环有了解吗?说说看!

事件循环对象之前专门写了一个文章说了,说到事件循环,不得不提到几个关联的知识点,就是任务队列,事件循环,宏观任务和微观任务,单线程等等

这边做一个简单的复习

js是一个单线程的脚本语言,但是不代表它只有一个线程,它处理同步任务的线程只有一个,叫做主线程,所有的微观任务都将产生一个回调函数,全部放在了任务队列中,当主线程任务结束的时候,会从任务队列中去查询有没有异步任务返回,如果有返回就会把异步任务转换为同步任务,放在主线程处理,那么去一遍遍的查询任务队列中是否有可用异步任务的这个动作称之为事件循环

说说你对target="_blank"的理解?有啥安全性问题?如何防范?

安全性问题就是,目标页面可以通过document.referer查询到源路径,解决办法就是在a中加noreferer;

假如设计稿使用了非标准的字体,你该如何去实现它?

协商/换图片/引入字体(字蛛)

Ajax与Flash的优缺点分别是什么?

ajax是异步请求,更新页面的方案,flash现在在与时代脱轨,但是仍有不少直播网站使用flash

列举CSS优化、提高性能的方法

  • 尽量少使用通配符和元素选择器
  • 少使用嵌套
  • 压缩css
  • 减少页面重排
  • 使用link,而不是import

请写出一个函数求出N的阶乘(即N!)

function test1(n){
    if(n > 1){
        return n * test1(n-1);
    }
    return n;
}

Form表单是怎么上传文件的?你了解它的原理吗?

在html表单中,使用input file上传图片,比如设置methods是post且enctype是multipart/form-data
浏览器才知道是使用了二进制发送表单数据
原理略

字符串相连有哪些方式?哪种最好?为什么?

es6的模板字符串,传统加号拼接兼容性更好些

From表单提交时为什么会刷新页面?怎么预防刷新?

在原来的web开发中,通常是form表单提交之后,后端返回一个新的页面,这种方式也被web程序员所接受,所以浏览器也是默认了这个规范,避免刷新只能监听submit,然后使用ajax请求发送

要是position跟display、overflow、float这些特性相互叠加后会怎么样?

display: none;之后所有内容不可见
定位之后,浮动无效

什么是事件委托?它有什么好处?能简单的写一个例子吗?

上面提到过冒泡,列表点击事件那个例子,就是事件委托,通过冒泡,父元素监听是谁委托
我把上面的复制一遍:

事件分为几个阶段,第一个是捕获阶段-》第二个是目标阶段-》第三个是冒泡阶段
冒泡是从内而外进行传递的,当点击子元素的事件,那么相应的父级元素的事件也会触发
冒泡可以使用event.stop*,一个方法,我忘记了。。。
但是一些业务场景来讲,比如列表,我们没必要去给每一列去绑定事件,可以在父级上定义事件,通过事件对>象的排查,确定是第几个元素点击了click,那么这将是优化长列表的一种常见的手段

web workers有用过吗?能帮我们解决哪些问题?

上一个问题讲过了,js是单线程的脚本语言,来处理同步任务,但是一些IO读取等同步任务可能会同步任务执行缓慢,为了利用多核cpu,webwoker允许我们把任务放在里面,不影响主线程的任务进行;

解决:复杂的io读取,或者大量计算,有利于运用多核CPU,来更高效的执行任务

如何使用自定义字体,有啥注意事项

@font-face有兼容问题,而且字体过大在app端可能运行失败,需要使用字蛛

document的onload和onready的区别

js中没有提供ready方法,但是jquery提供了,ready的意思是文档已经加载完毕,但是加载完毕并不代表资源全部加载完毕,ready后可以对dom进行操作;但是load指的是所有的资源(图片媒体等)全部加载完毕

HTML5的地理定位

navigator.getloction,不好用,定位不准确

css3的:nth-child和:nth-of-type的区别是什么?

查找父元素的第几个子元素,后者是根据类型来查找
()里面可以写数字即第几个,也可以写表达式等

写一个函数找出给定数组中的最大差值

这个很多

function test1(arr){
    return arr.sort(); // 排序,然后第一个减去最后一个
}
Math.max(...arr); // 求最大数
Math.min(...arr); // 最小数
然后相减即可

HTML5中新添加的表单属性有哪些?

placeholder,type类型中也添加了很多新属性,比如date,url,email

什么是视差滚动?如何实现视差滚动的效果?

视觉差滚动是今年来非常流行的web表现方式,通常是滑动页面,然后对应的元素进行偏移,这种效果我们称之为视觉滚动,通常会有很多库来实现它

写出4个使用this的典型例子

*.call(this);
funtion test1(){
    this.a = funtion(){
        return this.b
    }
    this.b = "test"
}
let a = 1;
console.log(this.a)
funtion test1(){
    a = funtion(){
        return this.b
    }
    b = "test"
}

渐进式渲染是什么?

web应用做兼容,先把最基础的兼容好,再做增强

margin和padding的使用场景有哪些?

margin:外部盒子与其他元素的距离
padding:内部盒子与内容的距离

JSONP的原理,解决了什么问题

解决了浏览器同源策略问题
缺点: 只支持get请求
原理:创建script标签,回调函数挂载到全局,给脚本设置url,并设置回调,脚本加载的回调会自动给全局变量,监听onload,加载之后移除script标签

function jsonp(url,parmas.callback){
  var script = document.createElement("script");
    script.url = url + parseObjParmas({...parmas, callback: "jsonp"});
    document.appendChild(script);
    window.jsonp = callback;
    srcipt.onload = () => {
    document.removeChild(script);
    }
}

你了解download属性吗?

h5新增下载功能,兼容不好,必须和href搭配,用于描述下载说明

inline、block、inline-block这三个属性值有什么区别?

规定了盒子的排列方式
inline: inline-inline;
block: block-block
inline-block

盒子分为内盒子和外盒子,而这些属性就控制着页面布局方式和内容布局方式

写一个方法,使得sum(x)(y)和sum(x,y)返回的结果相同

函数柯里化

console.log(sum(x)(y));
function sum(){
    let arg= arguments;
    return funtion(){
        let arr = [];
        arr.push(...arg, ...arguments);
        return arr.reduce((total, current) => {
            total += current;
        },0)
    }
}

HTML5相对于HTML4有哪些优势?

  1. 语义化
  2. 声明简洁
  3. 媒体画布
  4. 更好的被机器解码
  5. 全面拥抱移动端

box-sizing常用的属性有哪些?分别有什么作用?

border-box content-box
用于浏览器切换标准模式和怪异模式的
标准模式的盒子宽度是内容宽度不算padding和border,怪异模式是算在里面的,显然后者符合人类思维
所以一般会写border-box

请说下你对__proto__和prototype的理解

隐式原型和显式原型
每一个函数都有一个prototype指向函数原型,原型中的construtor指向构造函数
每一个对象都有一个_proto_指向构造这个对象的原型对象

隐式对象不可打印(谷歌和火狐可以)

有用过WebGL吗?说说你对它的理解

一种js的api,用来绘制2d,3d的图形

用过哪些css框架?

antdesign bootstrap layui

这两天一直在搜集关于JS的原型方面的知识,今天我来总结一下关于原型和原型链等周边相关知识,在这里做一个易于理解的解释。
如果文章有误区或者有错误,请评论欢迎指出;

首先原型是什么?

原型是js继承的基础,js的继承就是原型的继承

Function的原型对象

我们先来看一张图,画的比较草率

微信图片_20190929202953.jpg

首先我们创建了一个函数person,那我们通过打印这个函数,会发现里面有一个prototype这个属性,那么这个prototype指的是这个person函数的原型,那么我们的函数原型中有一个构造函数(construtor)指向的就是person函数

那么我们就知道了函数中的prototype就是原型,原型里面的构造函数指的其实就是我们声明的函数;
搞清楚这个关系之后,我们可以通过new操作符去创建一下构造函数指向新的对象:

var obj = new person();

打印这个obj显示的是_proto_,这个_proto_其实指的就是person中的原型,那么_proto_中的构造函数也就是person构造函数(印证了我们上一条的观点)


1. 当new了构造函数之后,和原构造函数没有什么关系了,它只和原构造函数中的原型有关系;
2. 当我们new了多个构造函数之后,它们的对象的隐式原型都指向原构造函数的原型,因此它们共享原型中的属性和方法;
3. 当我们访问对象中的属性的时候,如果对象本身没有,回去对象中的原型中去查询,如果都没找到则返回未定义,如果对象本身和原型中有同样的属性和方法,会返回对象中的不会返回原型中的(查找的过程就叫原型链
4. 我们在对象中想这样修改原型中的值是不可能的:p1.name = "test"; 它只能读取原型中的值不能修改,这样操作是在本身的对象中添加一个name属性


证明它们

// 声明一个函数
function person(){};
person.prototype.name = "原型中的name";
person.prototype.age = "原型中的age";
// 1. new构造函数得出的新对象共享原型的属性和方法
var person1 = new person();
var person2 = new person();
console.log("new构造函数得出的新对象共享原型的属性和方法:" + person1.name); // 原型中的    name
console.log("new构造函数得出的新对象共享原型的属性和方法:"+ person1.name); // 原型中的name
// 2. 对象添加/修改属性不会操作原型中的
person1.others = "对象中的others";
console.log("对象添加/修改属性不会操作原型中的:" + person1.others); // 对象中的others
// 3. 对象和原型中存在同一个属性会返回对象中的
person1.name = "对象中的name";
console.log("对象和原型中存在同一个属性会返回对象中的:" + person1.name); // 对象中的name


关于原型的一些属性/方法

prototype属性: 存在于任何函数(不仅仅是构造函数,其他函数我们不关注而已),指向的是函数中的原型;
construtor属性:指向的是原构造函数;
  function person(){};  
    console.log(person.prototype.construtor === person); // true
    var person1 = new person();
    console.log(person1 instanceof person); // true

如果要重新赋值新的prototype,比如这样:

person.prototype = {namespace: "1"};

那么会造成这样的情况:

var person1 = new person();
console.log(person.prototype.construtor === person); // false

为了避免这样的情况(构造函数指向不正确):

person.prototype = {construtor: person, namespace: "1"};


_proto_:隐式原型

所有对象都有一个隐式原型,指向了构造函数的原型,这个原型不可访问,只有在谷歌浏览器,firefox浏览器支持直接._proto_这样的方式去访问,尽量开发者不要操作_proto_,因为不慎会影响继承原型链;

var person1 = new person();
console.log(person1._proto_ === person.prototype); // true

hasOwnProperty:判断属性是否来自对象本身/还是继承了原型中的属性
var person1 = new person();
person1.name = "shenhao";
console.log("name属性是不是来自对象本身的呢?" + person1.hasOwnProperty("name")); // true
console.log("age属性是不是来自对象本身的呢?" + person1.hasOwnProperty("age")); // false
console.log("sex属性是不是来自对象本身的呢?" + person1.hasOwnProperty("sex")); // false

我们可以看到,hasOwnProperty只能判断对象中的属性,不能判断原型中的属性,而且如果对象中的属性如果为空那么也会返回false;

那么我们如何判断这个属性是原型上的属性呢?

in操作符:查找对象中的属性,如果查找不到会在原型中去查询
var person1 = new person();
person1.name = "shenhao";
person.prototype.sex = "男"
console.log("sex" in person1); // true

但是有一个问题,in操作符查询对象中的属性,如果找到了就会返回对象中的,就不会去查询原型中的了,所以我们这个方案也失败;

所以我们可以用2者结合的方式去写一个函数:

function hasProto(obj, context){
    if(!(context in obj)){
        // 如果原型和对象中都没有属性
        console.log(`${context}不存在${obj}的原型和对象上`)
    }else if(obj.hasOwnProperty(context)){
        console.log(`${context}存在${obj}的对象上`)
    }else {
        // in操作符查询了(成功),但是hasOwnProperty查找却是失败,那么此属性在原型中存在
        console.log(`${context}存在${obj}的原型上`)
    }
}

创建对象的几种“模式”

原型的模式,如果在原型中创建了几个属性和方法,所有的构造函数出来的对象都会共享它们,方法共享比较nice,但是属性共享往往是不符合业务的,所以这是它最大的一个缺陷

构造函数的模式:每一个构造函数都会创建自己独立的属性和函数,属性自己独一份这没有错误,但是可能同样的函数要写很多份,势必会造成浪费;

但是在构造函数的模式,我们可以对function进行一个封装

function person(){
    this.name = "shenhao";
    this.age = "12";
    this.story = story;
};

function story(){
    return "一个开心的故事"
}

var person1 = new person();
console.log(person1.story()); //一个开心的故事

作为面向对象的语言,为了性能把方法从对象抽离出来,是非常emmmmm的,你们懂的

组合模式: 封装构造函数(传参)+ 原型模式
function person(name, age){
    this.name = name;
    this.age = age;
};

person.prototype.story = function(context){
    return context;
}

var person1 = new person("shenhao", "18");
console.log(person1.story("123"));
动态原型模式: 更优的解决方案,把构造函数和原型都写在了构造函数中
// 声明一个函数
function person(name, age){
    this.name = name;
    this.age = age;
    if(typeof this.story !== "function"){
        // 说明是第一次创建这个对象, 在原型上绑定
        person.prototype.story = (context) => {
            return this.name + context
        }
    }
};

var person1 = new person("shenhao", "18");
console.log(person1.story("你大爷"));


ok, 全部结束,希望你能有所收获

我们需要补充一些前置知识,比如单线程模型是什么?
js是一个单线程的脚本语言,之所以为什么不是多线程而是单线程,是因为历史遗留的原因,脚本语言如果使用了多线程,那么一个线程操作了dom,第二个线程也操作了dom,那么浏览器改听谁的,如果是多线程会对开发者来说是一个弊大于利的事情;

那么没有了多线程就说明了,我们的任务需要在一个线程中进行,但是js虽然是单线程,但是还有很多线程,只是同一时间执行事件的线程只有一个,这个线程叫做主线程

但是我们会发现,如果现在是单线程,执行任务要等到上一个任务执行结束才会到下一个,这对于一些IO操作,ajax请求操作是非常坑的事情,我们需要等到这些任务执行结束才会执行下面的,等待IO/ajax返回并不需要消耗CPU,还需要花时间等它们,非常不划算,所以js引入了新的概念叫做消息队列(任务队列)

js在执行任务的时候,会把所有的同步任务优先执行,把所有的异步任务挂起到其他队列,等到我们的同步任务全部清空,再看异步任务是否满足条件再添加到主线程中的任务队列中执行,比较经典的例子就是setTimeout;

let timer = setTimeout(() => {
    console.log("这并不是准时的延迟噢")
}, 1000)

延时器的意思就是我需要等待一段时间把这样的任务放到主线程的最后面,那么这个参数传递的是1000,并不是真的1秒延迟执行,而是在主线程前面的任务执行完毕,如果前面有很多耗时的任务,那么这个1000指的就是最少时间而不是最终时间;

当我们的同步任务被主线程全部执行完毕,会去检查其他的队列中存在异步任务,如果检查出来异步任务满足条件那么就放到了主线程去执行,那么这个异步任务也就变成了同步任务,然后当主线程又一次清空了,又要去找异步任务去执行,如果一旦任务队列是空的,那么程序执行结束。每一个消息会与一个函数进行一个关联,等到执行到此任务(消息)的时候,会执行对应的函数,如果没有这个函数,那么这个消息就会遗失;

那么我们把这样的一次一次的去查询异步任务是否满足条件进入主线程执行任务的这个过程称之为事件循环机制

维基百科对事件循环机制定义是这样的

“Event Loop是一个程序结构,用于等待和发送消息和事件(a programming construct that waits     for and dispatches events or messages in a program)”。可以就把Event Loop理解成动态更新的消    息队列本身。

可以用代码这样表示

while(queue.waitForMessage()){
    queue.processNextMessage();
}


那么任务可以分为2类:

  1. 宏观任务 (同步) :
    没有被引擎挂起,在主线程中执行的任务,只有前面的任务执行完毕才能轮到下一个
  2. 微观任务 (异步)
    被js引擎挂起的任务,这些任务会在任务队列中,只有js引擎认为有条件进入主线程,那么就加入到主线程摇身一变就变成了同步任务,排在异步任务后面的代码,不用等前面的执行,会立即执行;

关于单线程问题,除了主线程,还有ajax线程,setTimeout线程,事件监听线程,这也都证明了js是单线程处理事件的语言,而不是只有一个线程;

20170804013026998.png

今天做题遇到了一个问题,vue的模板语法是基于什么的,带着强烈的好奇心我找到了它,mustache,胡子语法;
然后打开了官网,这个语法已经支持了20多种编程语言,js的支持是node的作者开源的;

微信截图_20190909220549.png

git地址: https://github.com/janl/mustache.js

模板引擎技术是非常有用的,所以它不是一个冷门的知识,反而是需要我们去了解的;

所以我们通过github的安装指令,一步一步的来实现基本的api

我搜查了很多资料,网上并没有一个使用npm包的方式来做demo,都是使用render来执行渲染,鉴于我们日常工作中使用npm比较多,我做一版npm的demo

首先npm init初始化一个空项目;

然后安装mustache

npm i mustache -s -d

在package.json中填写如下内容:

"scripts": {
    "build": "mustache dataView.json myTemplate.mustache>public/output.html",
  },

如同这个指示看到的一样,我们需要创建一个json文件,这个文件就是变量配置文件,还需要创建一个模板文件,这个模板文件相当于执行render函数的文件,这种方式更加一目了然;

我们创建它们,我使用了vscode,并且装了相关的mustache的插件,所以语法会有提示;

微信截图_20190909221112.png

我们首先在json文件中写入一个对象,里面写一个值,然后在模板文件中使用{{}}来执行渲染;

然后执行

npm run build

就会发现在public下面生成了一个html文件,如果报错,说明你的文件目录跟我的不一样

我们在json文件中,写入了这么多值,有普通的值,有布尔值,有数组等等

{
"name": "shenhao",
"age": "19",
"html": "<p>123</p>",
"isTrue": true,
"thisIsObject": {
    "name": "shenhao",
    "age": "19"
},
"isArray": [{
    "name" : "shenhao"
},{
    "name" : "shenhao"
},{
    "name" : "shenhao"
}]
}

我们在模板中写出了这些代码,我在模板中写了一个简单的html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
{{name}} 有一个 {{&html}}  
<br>
<hr>
 {{thisIsObject.name}} {{thisIsObject.age}}
 <br>
<hr>
-webkit- {{#isTrue}} 如果是真就显示了 {{/isTrue}}
 <br>
<hr>
循环一下下面的内容, 如果是数组,可以用.来表示循环的每一个元素
{{#isArray}} {{name}} {{/isArray}}
<br>
<hr>
{{!^}}与{{!#}}相反,如果变量是null、undefined、 false、和空数组讲输出结果

</body>
</html>

我们来讲解一下基本的api

{{name}}:会在json中查询对应的值,并且渲染
{{&html}}: html在json中如果式一个html标签,可以用这样的方式进行转义 (类似vue中的v-html)
{{#boolean}} 和 {{/boolean}}: 是一个组合,如果boolean为真那么它们之间的内容会渲染,否则不会
{{^boolean}}: 和上面用法一样,只不过是上面的else
{{object.name}}: 同样支持对象键值对的方式获取
{{#array}} 和 {{/array}}: 如果这样写是一个数组,那么不仅有判断boolean的真假,它会迭代中间可以写迭代中的每一个元素,每一个元素可以用{{.}}来获取,如果要获取迭代中的内容是一个键值对,那么可以直接使用{{name}}

这就是mustache简单的用法,上面有demo,你们可以对着demo敲一遍就能非常easy的理解了;
希望理解了这个技术,你可以在其他语言同样可以用到它!