该标签可声明三种DTD类型,分别表示严格版本、过渡版本以及基于框架的HTML版本。css
<!DOCTYPE>的用法:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
解析:在上面的声明中,声明了文档的根元素是 html,它在公共标识符被定义为 "-//W3C//DTD XHTML 1.0 Strict//EN" 的 DTD 中进行了定义。浏览器将明白如何寻找匹配此公共标识符的 DTD。若是找不到,浏览器将使用公共标识符后面的 URL 做为寻找 DTD 的位置。
-:表示组织名称未注册。Internet 工程任务组(IETF)和万维网协会(W3C)并不是注册的 ISO 组织。
+为默认,表示组织名称已注册。
DTD: 指定公开文本类,即所引用的对象类型。 默认为DTD。
HTML: 指定公开文本描述,即对所引用的公开文本的惟一描述性名称。后面可附带版本号。默认为HTML。
URL: 指定所引用对象的位置。
Strict:排除全部 W3C 专家但愿逐步淘汰的表明性属性和元素。
复制代码
简单的来讲是为了统一规则,共同使用了标准通用标记语言(SGML)的一种约定,来告诉浏览器使用的是哪种编程规范(DTD)。HTML5 不基于 SGML,因此不须要引用 DTD。HTML5提供的<!DOCTYPE html>
是标准模式,向后兼容的, 等同于开启了标准模式,而且向后兼容。html
语义化的含义就是用正确的标签作正确的事情,HTML语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;在没有样式CCS状况下也以一种文档格式显示,而且是容易阅读的。搜索引擎的爬虫依赖于标记来肯定上下文和各个关键字的权重,利于 SEO。使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。前端
HTML5添加了不少新的语法特征其中包括<video>
、<audio>
和<canvas>
元素,同时集成了SVG内容。这些元素是为了更容易的在网页中添加和处理多媒体和图片内容而添加的。其它新的元素如<section>
、<article>
、<header>
、和<nav>
则是为了丰富文档的数据内容。新的属性的添加也是为了一样的目的。vue
HTML5除了更新了不少新的语义化标签也有不少新标签和特性: W3school里边有详细的介绍。能够大体的了解一下。重点关注<video>
、<audio>
、<canvas>
、localStorage
和sessionStorage
。html5
BOM是指浏览器对象模型,是用于描述这种对象与对象之间层次关系的模型,浏览器对象模型提供了独立的内容的、可与浏览器窗口进行互动的对象结构。 详情参考 node
DOM全称DocumentObjectModel(文档对象模型),是为HTML和XML提供的API的标准。 本文说的DOM默认为HTMl DOM。 jquery
CSS是前端三大件之一,一个页面是否美观CSS起到了决定性的做用。css3
通配符*{color: red;}
;nginx
元素选择器div{color : red}
;git
类选择器.high{color: red;}
;
id选择器#test{color: red;}
;
并联选择器div, .high{color: red;}
;
复合选择器div.high{background-color: green}
;
后代选择器div p {background-color: blue;}
;
子元素选择器h1 > strong {color:red;}
;
相邻兄弟选择器h1 + p {margin-top:50px;}
属性选择器a[href] {color:red;}
;
动态伪类选择器:详情请看大漠老师的伪类选择器
.demo a:link {color:gray;}/*连接没有被访问时前景色为灰色*/
.demo a:visited{color:yellow;}/*连接被访问事后前景色为黄色*/
.demo a:hover{color:green;}/*鼠标悬浮在连接上时前景色为绿色*/
.demo a:active{color:blue;}/*鼠标点中激活连接那一下前景色为蓝色*/
复制代码
通俗的讲:从0开始,一个行内样式+1000,一个id+100,一个属性选择器/class或者伪类+10,一个元素名,或者伪元素+1。
能够参考大漠老师的这篇文章你应该知道的一些事情——CSS权重。
CSS盒子模型是经典面试题之一,回答这个问题首先要了解什么是盒子模型:简单地说每一个html标签都是一个方块,而后这个方块又包着几个小方块。分别是:margin、border、padding、content。它们的关系是margin包着border包着padding包着content。就像盒子一层一层地包着同样,这就是咱们所说的盒模型。
CSS盒子模型包括w3c标准盒子模型和怪异盒子模型又叫IE盒子模型,他们的差异在于:在w3c和模型中,设置的width/height是content的宽度/高度,在怪异模式中width/height设置的是content+padding+border宽度/高度。
div{display:table-cell; width:1em; height:1em; border:1px solid #beceeb; font-size:144px; text-align:center; vertical-align:middle;}
div img{vertical-align:middle;}
复制代码
原来一直不是很清楚深拷贝和浅拷贝,在掘金上看了一篇文章js 深拷贝 vs 浅拷贝之后豁然开朗。弄明白这两个概念的时候须要了解什么是内存管理,任何一门计算机语言都会有内存管理机制,一样的js也不例外。js的内存管理机制是:JavaScript建立变量(对象,字符串等)时分配内存,而且在再也不使用它们时“自动”释放。 后一个过程称为垃圾回收。js内存空间分为栈(stack)、堆(heap)。其中栈存放变量,堆存放复杂对象。
这些值都有固定的大小,每每都保存在栈内存中(闭包除外),由系统自动分配内存空间。咱们能够直接操做保存内存空间的值,所以基础数据类型都是按值访问,数据在栈内存中的存储与使用方法相似于数据结构中的堆栈数据结构,遵循后进先出的原则。(基础数据类型:Number、String、Null、Undefined、Boolean、symbol)。
他们值的大小都不是固定的。引用数据类型的值是保存在堆内存中的对象,变量其实是存放在栈内存的指针,这个指针指向堆内存中的地址。
var a1 = 0; // 栈
var a2 = 'this is string'; // 栈
var a3 = null; // 栈
var b = { m: 20 }; // 变量b存在于栈中,{m: 20} 做为对象存在于堆内存中
var c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3] 做为对象存在于堆内存中
复制代码
不少人认为赋值就是浅拷贝,这是错误的认识;在咱们进行赋值操做的时候,基本数据类型的赋值(=)是在内存中新开辟一段栈内存,而后再将值赋值到新的栈中。这就是值传递。
var a = 10;
var b = a;
a ++ ;
console.log(a); // 11
console.log(b); // 10
复制代码
引用类型则是地址传递,将存放在栈内存中的地址赋值给接收的变量。
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
var obj3 = shallowCopy(obj1);
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
obj2.name = "lisi";
obj3.age = "20";
obj2.language[1] = ["二","三"];
obj3.language[2] = ["四","五"];
console.log(obj1);
//obj1 = {
// 'name' : 'lisi',
// 'age' : '18',
// 'language' : [1,["二","三"],["四","五"]],
//};
console.log(obj2);
//obj2 = {
// 'name' : 'lisi',
// 'age' : '18',
// 'language' : [1,["二","三"],["四","五"]],
//};
console.log(obj3);
//obj3 = {
// 'name' : 'zhangsan',
// 'age' : '20',
// 'language' : [1,["二","三"],["四","五"]],
//};
复制代码
先定义个一个原始的对象 obj1,而后使用赋值获得第二个对象 obj2,而后经过浅拷贝,将 obj1 里面的属性都赋值到 obj3 中。也就是说:
obj1:原始数据
obj2:赋值操做获得 obj3:浅拷贝获得
而后咱们改变 obj2 的 name 属性和 obj3 的 name 属性,能够看到,改变赋值获得的对象 obj2 同时也会改变原始值 obj1,而改变浅拷贝获得的的 obj3 则不会改变原始对象 obj1。这就能够说明赋值获得的对象 obj2 只是将指针改变,其引用的仍然是同一个对象,而浅拷贝获得的的 obj3 则是从新建立了新对象。 然而,咱们接下来来看一下改变引用类型会是什么状况呢,我又改变了赋值获得的对象 obj2 和浅拷贝获得的 obj3 中的 language 属性的第二个值和第三个值(language 是一个数组,也就是引用类型)。结果见输出,能够看出来,不管是修改赋值获得的对象 obj2 和浅拷贝获得的 obj3 都会改变原始数据。 这是由于浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。因此就会出现改变浅拷贝获得的 obj3 中的引用类型时,会使原始数据获得改变。
深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,
浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象
Object.assign
Object.assign:用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象(target),并返回合并后的target。用法: Object.assign(target, source1, source2); 因此 copyObj = Object.assign({}, obj); 这段代码将会把obj中的一级属性都拷贝到 {}中,而后将其返回赋给copyObj。 对于Object.assign()而言, 若是对象的属性值为简单类型(string, number),经过Object.assign({},srcObj);获得的新对象为‘深拷贝’;若是属性值为对象或其它引用类型,那对于这个对象而言实际上是浅拷贝的。这是Object.assign()特别值得注意的地方。{ ...obj }
$.extend( {}, ...)
使用jq中的$.extend(true, {}, ...)
使用lodash中的_.cloneDeepWith(value, [customizer])
JSON对象的parse和stringify
//例1
var source = { name:"source", child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
target.name = "target"; //改变target的name属性
console.log(source.name); //source
console.log(target.name); //target
target.child.name = "target child"; //改变target的child
console.log(source.child.name); //child
console.log(target.child.name); //target child
//例2
var source = { name:function(){console.log(1);}, child:{ name:"child" } }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
//例3
var source = { name:function(){console.log(1);}, child:new RegExp("e") }
var target = JSON.parse(JSON.stringify(source));
console.log(target.name); //undefined
console.log(target.child); //Object {}
复制代码
跨域是指一个域下的文档或者脚本试图去请求另外一个域下的资源行为。 跨域问题是因为浏览器的同源策略形成的,同源策略分为DOM同源策略和XmlHttpRequest同源策略。其中:
1.DOM同源策略是指禁止对不一样源页面DOM进行操做。这里主要场景是iframe跨域的状况,不一样域名下的iframe是限制互相访问的。
2.XMLHttpRequest同源策略是指禁使用XHR对象向不一样源的服务器地址发起HTTP请求。
只要协议、域名、端口有任何一个不一样,都被看成是不一样的域,之间的请求就是跨域操做。
1.跨域资源共享(CORS)
CORS(Cross-Origin Resource Sharing)
跨域资源共享,定义了必须在访问跨域资源市,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功仍是失败。
服务器端对于CORS的支持,主要经过设置Access-Control-Allow-Origin
来进行的。若是浏览器检测到相应的设置,就能够容许Ajax进行跨域的访问。
2.经过jsonp跨域
jsonp可以跨域的原理是用了<script>
标签来实现跨域请求的,除了<script>
能够跨域的表情还包括<link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链也属于跨域
。jsonp由回调函数和数据两部分组成,回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数的json数据。 优势:简单兼容性比较好,可用于解决主流浏览器的跨域访问问题。 缺点:仅支持get方法具备局限性,不安全可能会遭受XSS攻击。
3.document.domain + iframe跨域 4.location.hash + iframe 5.window.name + iframe跨域 6.postMessage跨域 7.nginx代理跨域 8.nodejs中间件代理跨域 9.WebSocket协议跨域
一句话解释闭包:闭包就是可以读取其余函数内部变量的函数。在下面的代码中f2就是闭包。
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
复制代码
闭包的主要有两大用处,一个是能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。
因为JavaScript的语言执行环境是“单线程”,因此若是有一个任务比较长就会形成阻塞,为了解决任务阻塞这个问题,JavaScript将任务的模式分为:同步和异步。 同步模式就是后一个任务等待前一个任务结束后再执行,程序的响应是与任务的排列顺序是一致的、同步的。异步模式彻底不一样,每个任务有一个或者多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,因此程序的顺序与任务的执行殊勋是不一致的、异步的。
异步编程的4种方法:
1.回调函数
回调函数是异步编程的基本方法。
假设有两个函数f1和f2,后者等待前者的执行结果。若是f1是一个很耗时的任务,能够考虑改写f1,把f2写成f1的回调函数。
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}
复制代码
采用这样的方式,把同步操做改为了异步操做,f1不会阻塞程序运行,至关于先执行程序的主要逻辑,讲耗时的操做推迟执行。 后调函数的优势是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,并且每一个任务只能指定一个回调函数。
2.事件监听
另外一种思路就是采用事件驱动模式。任务执行不取决于代码的顺序,而取决与某个事件是否发生。例如:为f1绑定一个事件。
f1.on('done', f2);
复制代码
这样的优势是比较容易理解,能够绑定多个事件,每一个事件能够指定多个回调函数,并且能够去耦合,有利于实现模块化。缺点是整个程序都要变成事件驱动,运行流程会变得不清晰。
3.发布/订阅
咱们假设纯在一个信号发射函数,当某个任务执行完毕,就像信号中心发布一个信号,其余任务能够向信号中心订阅这个信号,从而知道何时本身能够开始执行。这就叫作发布/订阅模式,又称观察者模式。
这个模式有多种实现方式,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。
首先,f2像信号中心的jQuery订阅done信号。
jQuery.subscribe("done", f2);
复制代码
而后f1进行以下改写:
function f1(){
setTimeout(function () {
//f1的任务代码
jQuery.publish("done");
}, 1000);
}
复制代码
jQuery.publish("done")的意思是,f1执行完成后,向信号中心jQuery发布done信号,从而引起f2的执行。 此外,f2完成后,也能够取消订阅。
jQuery.unsubscribe("done", f2);
复制代码
这种方法的性质跟事件监听相似,可是明显优于后者,由于咱们能够经过查看消息中心,了解存在多少信号、每一个信号有多少订阅者,从而监控程序的运行。
4.Promise对象
Promise对象是CommonJS工做组提出的一种规范,目的是为异步编程提供统一接口。 简单的说,它的思想就是每个异步任务返回一个Promise对象,该对象有一个then方法,容许指定回调函数。好比,f1的回调函数f2,能够写成:
f1().then(f2);
复制代码
f1要进行以下改写:
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}
复制代码
这样写的优势在于,回调函数就变成了链式写法,程序的流程能够看得很清楚,还有其余的一些很强大的方法可使用,Promise其余方法。并且,它还有一个前面三种方法都没有的好处:若是一个任务已经完成,再添加回调函数,该函数就会当即执行。因此不用担忧是否错过了摸个事件或信号。这种写法的缺点是编写和理解都相对比较困难。
1.Promise对象有三种状态,他们分别是:
这三种状态不受外界影响,并且状态只能从pending改变为resolved或rejected,而且不可逆。在Promise对象的构造函数中,resolved和rejected就是用来处理Promise的状态变化。
2.new Promise()里的函数是马上执行的
<script>
new Promise(function(resolve,reject){
$.ajax({
type:'post',
dataType: 'jsonp',
url:'http://api.money.126.net/data/feed/0000001,1399001',
data:{
},
success : function(res){
console.log(res) ;
resolve(res) ;
},
error:function(res){
reject(res) ;
}
})
});
</script>
复制代码
3.Promise.all()与Promise.race()的用法,更多方法ES6入门
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
<script>
var p1 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
} ;
var p2 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, 'P2');
});
} ;
// 同时执行p1和p2,并在它们都完成后执行then
var start = function(){
Promise.all([p1(), p2()]).then(function (results) {
console.log(results); // 得到一个Array: ['P1', 'P2']
}).catch(function(reason){
// ...
});;
}
</script>
复制代码
<script>
var p1 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
} ;
var p2 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, 'P2');
});
} ;
var start = function(){
Promise.race([p1(), p2()]).then(function (results) {
console.log(results); // 'P1'
}).catch(function(reason){
// ...
});;
}
</script>
复制代码
Promise 的最大问题就是代码冗余,原来的任务被Promise保证一下,无论什么操做,一眼望去都是then,原来的语义变得很不清楚,Generator函数恰好能够解决这样的问题,它能够多个线程互相协做,完成异步任务。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
复制代码
上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。
而后,Generator 函数的调用方法与普通函数同样,也是在函数名后面加上一对圆括号。不一样的是,调用 Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法能够恢复执行。 更多内容产考阮一峰老师的ES6入门 Generator 函数的语法
async一句话,它就是 Generator 函数的语法糖。async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。和Generator函数同样,async函数返回一个Promise对象,可使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等触发的异步操做完成,再接着执行函数后面的语句。
async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
getStockPriceByName('goog').then(function (result){
console.log(result);
});
复制代码
Tips:await 命令后面的 Promise 对象,运行结果多是 rejected,因此最好把 await 命令放在 try...catch 代码块中。 await 命令只能用在 async 函数之中,若是用在普通函数,就会报错。 若是将 forEach 方法的参数改为 async 函数,也有问题。正确的写法是采用for循环。
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}
复制代码
若是确实但愿多个请求并发执行,可使用 Promise.all 方法。
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
// 或者使用下面的写法
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(results);
}
复制代码
get和post的区别一直以来都是一个老生常谈的问题,具体的区别为:
“1. GET使用URL或Cookie传参,而POST将数据放在BODY中”,这个是由于HTTP协议用法的约定。并不是它们的自己区别。
“2. GET方式提交的数据有长度限制,则POST的数据则能够很是大”,这个是由于它们使用的操做系统和浏览器设置的不一样引发的区别。也不是GET和POST自己的区别。
“3. POST比GET安全,由于数据在地址栏上不可见”,这个说法没毛病,但依然不是GET和POST自己的区别。
GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。这个是它们本质区别,上面的只是在使用上的区别。 理解幂等性
什么是幂等性?幂等性是指一次和屡次请求某一个资源应该具备一样的反作用。简单来讲意味着对同一URL的多个请求应该返回一样的结果。
原型(prototype):在JavaScript中,每当定义一个对象(函数)的时候,对象中都会包含一些预约义的属性。其中函数对象的一个属性的就是原型对象prototype。普通对象没有prototype,但有proto属性。
为何只有函数才有prototype?由于当建立函数时,js会为这个函数自动添加prototype属性,值是一个有constructor属性的对象,不是空对象。而一旦你把这个函数看成构造函数(constructor)调用即经过new关键字调用,那么js就会帮你建立改构造函数的实例,实例继承构造函数prototype的全部属性和方法(实例经过设置本身的__proto__指向构造函数的prototype来实现这种继承)。
js正是经过__proto__和prototype的合做实现了原型链,以及对象的继承。
原型链:JavaScript对象有一个执行一个原型对象的链。当试图访问一个对象的属性时,它不只仅在该对象上搜寻,还会搜寻该对象的原型,以及对象的原型的原型,依次向上搜寻,直到找到一个名字匹配的属性或到达原型的末尾。
首先定义一个父类
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
复制代码
1.原型链继承 核心:将父类的实例做为子类的原型
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
复制代码
优势:
1.很是简单纯粹的继承关系,实例是子类的实例,也是父类的实例
2.父类新增的原型方法或原型属性,子类都能访问到
缺点:
1.要想为子类新增属性和方法,必需要在new Animal()这样的语句以后执行,不能放到构造器中
2.建立子类实例时,没法向父类构造函数传参
3.没法实现多继承
4.来自原型对象的全部属性被全部实例共享
2.构造继承
原理:使用父类的构造函数来加强子类实例,等因而复制父类的实例属性给子类
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
复制代码
优势:
1.解决了1中,子类实例共享父类引用属性的问题
2.建立子类实例时,能够向父类传递参数
3.能够实现多继承(call多个父类对象)
缺点:
1.实例并非父类的实例,只是子类的实例
2.只能继承父类的实例属性和方法,不能继承原型属性/方法
3.没法实现函数复用,每一个子类都有父类实例函数的副本,影响性能
3.实例继承 原理:为父类实例添加新特性,做为子类实例返回
function Cat(name){
var instance = new Animal();
instance.name = name || 'Tom';
return instance;
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false
复制代码
优势:
4.拷贝继承
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
复制代码
优势:
1.支持多继承 缺点:
1.效率较低,内存占用高(由于要拷贝父类的属性)
2.没法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
5.组合继承 原理:经过调用父类构造,继承父类的属性并保留传参的优势,而后经过将父类实例做为子类原型,实现函数复用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
复制代码
优势:
1.弥补了方式2的缺陷,能够继承实例属性/方法,也能够继承原型属性/方法 既是子类的实例,也是父类的实例
2.不存在引用属性共享问题
3.可传参
4.函数可复用 缺点:
1.调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
6.寄生组合继承 原理:经过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点。
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
(function(){
// 建立一个没有实例方法的类
var Super = function(){};
Super.prototype = Animal.prototype;
//将实例做为子类的原型
Cat.prototype = new Super();
})();
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true
复制代码
MVVM是Model-View-ViewModel的缩写。
Model 表明数据模型,也能够在Model中定义数据修改和操做的业务逻辑。
View 表明UI 组件,它负责将数据模型转化成UI 展示出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,链接Model和View。
在MVVM框架下View和Model没有直接的联系,而是经过ViewModel进行交互的,Model和ViewModel的交互是双向的,所以View数据的变化会同步在Model中,而Model 数据的变化也会当即反应到View 上。
ViewModel 经过双向数据绑定把 View 层和 Model 层链接了起来,而View 和 Model 之间的同步工做彻底是自动的,无需人为干涉,所以开发者只需关注业务逻辑,不须要手动操做DOM,不须要关注数据状态的同步问题,复杂的数据状态维护彻底由 MVVM 来统一管理。
vue采用数据劫持组合发布-订阅模式的方式,经过Object.defineProperty()来劫持各个属性的setter、getter,在数据变更时发布消息给订阅者,触发响应的监听回调。这也是vue不支持IE8以及以更低的浏览器的缘由。Vue双向数据绑定将MVVM作为数据绑定入口,整合Observer,Compile和Watcher三者,经过Observer来监听model层的数据变化,而后经过Compile来解析编译模板指令(vue中是用来解析 {{}})最终利用Watcher搭建Observer,Compile之间的桥梁达到数据变化-->视图更新,视图交互-->数据model更新双向绑定效果。
因为数组只要不是从新赋值一个新的数组对象,任何对数组内部的修改都不会触发setter方法的执行。因此vue单独的从新修改监听了 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 7 种方法。
beforeCreate(建立前) 在数据观测和初始化事件还未开始
created(建立后) 完成数据观测,属性和方法的运算,初始化事件,$el属性尚未显示出来
beforeMount(载入前) 在挂载开始以前被调用,相关的render函数首次被调用。实例已完成如下的配置:编译模板,把data里面的数据和模板生成html。注意此时尚未挂载html到页面上。
mounted(载入后) 在el 被新建立的 vm.$el 替换,并挂载到实例上去以后调用。实例已完成如下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程当中进行ajax交互。
beforeUpdate(更新前) 在数据更新以前调用,发生在虚拟DOM从新渲染和打补丁以前。能够在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在因为数据更改致使的虚拟DOM从新渲染和打补丁以后调用。调用时,组件DOM已经更新,因此能够执行依赖于DOM的操做。然而在大多数状况下,应该避免在此期间更改状态,由于这可能会致使更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁以前调用。实例仍然彻底可用。
destroyed(销毁后) 在实例销毁以后调用。调用后,全部的事件监听器会被移除,全部的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
答: Vue 实例从建立到销毁的过程,就是生命周期。从开始建立、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
答:它的生命周期中有多个事件钩子,让咱们在控制整个Vue实例的过程时更容易造成好的逻辑。
答:它能够总共分为8个阶段:建立前/后, 载入前/后,更新前/后,销毁前/销毁后。
答:会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
答:DOM 渲染在 mounted 中就已经完成了。
简单的说是因为js的堆和栈决定的,在js中基本数据类型存放在栈中。当vue初始化一个实例时会初始化一个date对象,若是这个实例在不少地方调用就会照成某个实例改变了状态后影响其余实例。为了解决这个问题vue须要返回一个函数从而返回一个新的数据对象。
当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,由于组件可能被用来建立多个实例。若是 data 仍然是一个纯粹的对象,则全部的实例将共享引用同一个数据对象!经过提供 data 函数,每次建立一个新实例后,咱们可以调用 data 函数,从而返回初始数据的一个全新副本数据对象。
vue路由问题官网介绍的很是详细,点击就可查看 vue路由
vue路由的实现原理:
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用 window.location.hash 读取。特色:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动做,对服务端安全无用,hash不会重加载页面。
history模式:history采用HTML5的新特性;且提供了两个新方法: pushState(), replaceState()能够对浏览器历史记录栈进行修改,以及popState事件的监听到状态变动。
一、与AngularJS的区别
相同点:都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不一样点:AngularJS的学习成本高,好比增长了Dependency Injection特性,而Vue.js自己提供的API都比较简单、直观;在性能上,AngularJS依赖对数据作脏检查,因此Watcher越多越慢;Vue.js使用基于依赖追踪的观察而且使用异步队列更新,全部的数据都是独立触发的。
二、与React的区别
相同点:React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,二者都须要编译后使用;中心思想相同:一切都是组件,组件实例之间能够嵌套;都提供合理的钩子函数,可让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
不一样点:React采用的Virtual DOM会对渲染出来的结果作脏检查;Vue.js在模板中提供了指令,过滤器等,能够很是方便,快捷地操做Virtual DOM。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
在main.js引入store,注入。新建了一个目录store就可使用vuex了。
state Vuex 使用单一状态树,即每一个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不能够直接修改里面的数据。
mutations mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters 相似vue的计算属性,主要用来过滤一些数据。
action actions能够理解为经过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操做数据。view 层经过 store.dispath 来分发 action。
const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
复制代码
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
})
复制代码