(1)、react的优势
React优点:
1.声明式设计 −React采用声明范式,可以轻松描述应用。
2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
3.灵活 −React可以与已知的库或框架很好地配合。
4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
我们在实际项目开发选型中一般大型项目会选择React。
(2)、diff算法的理解
diff算法用来计算出Virtual DOM中改变的部分,然后针对该部分进行DOM操作,而不用重新渲染整个页面,渲染整个DOM结构的过程中开销是很大的,需要浏览器对DOM结构进行重绘与回流,而diff算法能够使得操作过程中只更新修改的那部分DOM结构而不更新整个DOM,这样能够最小化操作DOM结构,能够最大程度上减少浏览器重绘与回流的规模
(3)、为什么要单向数据流、组件交互。
单项数据流不允许子组件修改父组件传入的数据,避免数据出现高度耦合 。
通过输入型绑定把数据从父组件传到子组件。
(4)、prop和state的区别
1、prop用于定义外部接口,state用于记录内部状态;
2、prop的赋值在外部世界使用组件时,state的赋值在组件内部;
3、组件不应该改变prop的值,但是state的存在目的就是让组件来改变的
(5)、前端工程化的理解、如何自己实现一个文件打包,比如一个JS文件里同时又ES5 和ES6写的代码,如何编译兼容他们?
Jenkins
1、引用browser.js
2、引用browser-polyfill.js
两个文件基本就解决了浏览器对ES6的大部分支持问题。
(6)、如何实现垂直和水平居中,多种方法。
水平居中
如果是行内元素,要实现水平居中只需要将父元素设置为text-align=center
如果是固定宽度的块状元素,设置该元素本身为margin: 0 auto
css3的新属性font-content,自动将元素宽度缩小到内容的宽度,然后使用margin:0 auto可以轻松的实现水平居中(目前只支持chrome和FireFox)
.son{ width: -moz-fit-content; width: -webkit-fit-content; width:fit-content; margin:0 auto; }
4.绝对定位以及margin-left的负值实现水平居中
.son {
position: absolute;
width: 50px;
left: 50%;
margin-left: -25px;(宽度的一半)
background-color: blue;
text-align: center;
}
5.绝对定位left right同时设置为0 同时设置margin:0 auto
.son{
position: absolute;
width: 50px;
left: 0;
right: 0;
background-color: blue;
margin: 0 auto;
height: 100%;
}
垂直居中
若元素为单行文本,直接设置其text-align为父元素的高度
利用position以及top bottom属性
.son{
position:absolute;
height:固定;
top:0;
bottom:0;
margin:auto 0;
}
3.利用margin的负值
.son{
position:absolute;
top:50%;
height:固定;
margin-top:-0.5高度;
}
4.利用vertical-align
.parent::after, .son{
display:inline-block;
vertical-align:middle;
}
.parent::after{
content:'';
height:100%;
}
(7)、流式布局如何实现,响应式布局如何实现?
流式布局(百分比布局)
在CSS2时代就有,主要指的是将页面中元素的宽度以百分比表示并进行排版,可以在不同分辨率下显示相同的版式
响应式布局
关键技术是CSS3中的媒体查询,可以在不同分辨率下对元素重新设置样式(不只是尺寸),在不同屏幕下可以显示不同版式
(8)、对移动端开发了解多少?
1、关于meta标签
在移动端开发中,会涉及到meta标签,这是HTML5里的元信息标签,会告诉浏览器一些有用的信息让浏览器作出不同于默认的渲染效果,在移动端开发中需要加入一个必备的元信息:
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
这个标签是让HMTL文档的宽度等于设备的宽度,缩放比例是1,而且不允许用户自行缩放。
另外的需要加的meta标签是让页面在IOS系统中的手机浏览器中全屏显示,加入:
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-touch-fullscreen" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
防止页面中的数字和邮箱变成链接情况,需要加入:
<meta name="format-detection" content="telephone=no, email=no">
2、devicePixelRatio的理解
devicePixelRatio是设备像素比的意思,指的是设备上物理像素和设备独立像素的比例。手机的宽高物理像素就是手机参数里的分辨率的数值,比如:IphoneX像素是2436x1125像素,那么设备的横向有1125个物理像素,设备的纵向有2436个物理像素。在浏览器中,通过window.devicePixelRatio获取设备像素比的值,IphoneX的devicePixelRatio值为3,那么设备的独立像素是812x375像素,这个像素是css3中的“px”值,例如,要想设置IphoneX中某个元素的宽与文档宽一样width设为375px。
3、rem的使用
rem可以用来设置元素的各种尺寸的值,它是以html元素的字体尺寸为准,例如html的字体设为12px,那么1rem=12px。移动设备多种多样,一般设计稿就只有一套,这一套设备稿是针对某一个部设备的,网上用来举例最多的是iphone6。例如:要设置一个元素宽度与设备宽度一致(为了说明,有意不使用百分比),那么,iphone6的设置是 width: 375px。但是在iphone 6 plus打开就会发现元素的宽度并不等于设备的宽度。要解决这个问题,在这里有一种解决思路:用JS动态改变html的字体尺寸,尺寸样式用单位"rem"代替"px",当html字体的尺寸改变了,那么元素的宽度就改变了。
顺着上面的思路,假设设计稿是以Iphone6的尺寸设计出来的。我要设置元素的宽度用rem作单元,但是值为多少合适一点呢?网上说到的“网易方案”,用100px为参考,这样方便看着设计稿不用作为太复杂的计算就可以得出对应的值。Iphone6在设计稿上的宽度应该是750px,那么我就设置元素的width设为7.5em。要是换到Iphone6 plus设备上,html的font-size就要改变了,公式应该是 window.screen.availWidth / 375 * 100 / 2(注:window.screen.availWidth是设备的独立像素,2是Iphone6的设备像素比,这里用到375和2皆因假设设计稿是Iphone6),所以在文档加载之后,加入以下的代码:
document.documentElement.style.fontSize = window.screen.availWidth / 375 * 100 / 2 + 'px';
这样修改了html字体的尺寸,而不需要修改css的代码,也不需要用媒体查询。
网上也提及到一种修改 initial-scale的方式,具体思路也是通过rem实现,只不过不是改变html的字体尺寸,而是改变缩放的比例,同样是以Iphone6的设计稿为例,样式一开始就要加入:
html{
font-size: calc(100px / 2) ;
}
然后用JS改写meta里的属性:
let scale = window.screen.availWidth / 375
document.querySelector('[name="viewport"]').setAttribute('content', `width=device-width, initial-scale=${scale}, user-scalable=no`)
用这种方式实现,会发现最后元素的尺寸数值固定为Iphone6设备中的像素值,只是做了元素的显示缩放。
4、媒体查询
媒体查询可以根据不同的设备加载使用不同的css样式渲染页面,这种方式应该结合flex布局,使用百分比作单位,或者结合其它UI框架例如:bootstrap,antd,elementUI等等,这些框架提供了一些内置的类名方便控制不同设备大小的样式。
(9)、跨域的方法:自己实现JSONP,如何设计?为什么要跨域?为什么JS会对跨域做出限制?
说出来很简单,那就是动态创建<script src="你ajax发起GET请求的地址"></script>
这个DOM。
这样的话,浏览器就允许script标签请求跨域的内容,但是如何拿到服务端返回的内容并让浏览器端开发人员做点事情呢。
这里用到另一个技巧,很土的办法:那就是让服务端返回的内容就直接是一个函数调用的语法。
比如:服务端返回这样的内容-----> YourFunc({服务端要返回给客户的对象}){}
Demo:YourFunc({"a":1,"b":2})
然后客户端需要提前动态在全局空间内注册一个函数,其名称为YourFunc(data){}
. 这样当<script src="ajaxurl"></script>
请求返回数据后,浏览器解析其中的内容,会自动执行YourFunc({a:1,b:2})
, 这样就调用了你提前注册好的函数YourFunc(data){};
你在全局空间中注册的函数可以这样写:YourFunc(rel){ 执行服务器结果处理的事情 }
我的JsonP封装:
我对Ajax请求的创建和执行进行了函数封装,以方便开发人员使用。ajax请求函数如下:
需要注意的是:在我的ajax函数使用时需要遵循一个约定,那就是服务端返回的数据约定为:
{
"success":true or false,
"errormsg":your logic error,
"data":your data when success,if Failed put it null is ok.
}
//函数形参说明:
//Ajax请求实现跨域(jsonP因为其实现机制原因故只能是GET请求)
//url-----,字符串,用户要ajax请求的URL
//callbackName,字符串,设置一个回调函数名。您只需要保证服务器端使用同名函数来包装json对象即可。一般服务器端可以类似于这样设置: response.write($Request[‘callback’]+"({json对象})"),可保证与客户端设置一致。
//onsuccessfun,函数对象,表示请求成功后要执行的回调。(200状态码,且json状态为success)
//onlogicError,函数对象,表示请求的逻辑失败后的回调。(也是200状态码,但json状态为fail)
function ajaxjsonp(url,callbackName,onsuccessfun,onlogicError) {
//提前创建好回调函数
window[callbackName] = function (rel) {
if (rel.success == true) {
onsuccessfun(rel.data);//调用请求成功的处理函数
} else {
onlogicError(rel.errormsg);//调用逻辑错误处理的函数
}
};
//构造一个DOM向服务器发请求
var scriptdom = document.createElement("script");
var callbackRequest = "callback="+callbackName;
url = url.indexOf("?")>0?url + "&" + callbackRequest:url+"?"+callbackRequest;
scriptdom.src = url;//利用DOM向服务器发起GET请求
document.body.appendChild(scriptdom);
}
(10)、原型、原型链、继承如何实现?
原型重点: JavaScript 引用类型 Object
原型指向可以被改变的
实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象
构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(proto)指向也会发生改变
实例对象和原型对象之间的关系是通过__proto__ 原型来联系起来的,这个关系就是原型链
如果原型指向改变了,那么就应该再原型改变指向之后添加原型方法
实现继承
小知识---->instanceof的判断方法:
从左边操作数的__proto__路线出发,从右边操作数的prototype出发,如果两条路线最终指向一个引用就是true了
1.利用 call 借用构造函数继承
优点:实现了继承属性,但值都不相同
缺点: 无法继承父级类别中原型上的方法
1. function Person(name,age,sex,weight){
2. this.name=name;
3. this.age=age;
4. this.sex=sex;
5. this.weight=weight;
6. }
7. Person.prototype.sayHi=function(){
8. console.log("您好")
9. }
10.
11. function Student(name,age,sex,weight,score){
12. //将当前实例对象传入Person 借过来使用一次来达到继承效果
13. Person.call(this,name,age,sex,weight);
14. this.score=score;
15. }
16.
17. var stu1=new Student("小明",10,"男","10kg","100")
- prototype 实现继承
利用prototype,将Student 的prototype 指向 Person 来达到继承效果,
优点:继承了父级原型上的方法
缺点: 实例化多个Student 都必须共用相同的name 和 age
Student.prototype.constructor=Student
注意: 使用原型继承时,需要将构造器的指向更改回正确的指向
1. function Person(name,age){
2. this.name=name;
3. this.age=age;
4. }
5.
6. Person.prototype.eat=function(){
7. console.log("Person 吃饭")
8. }
9.
10. function Student(num,score){
11. this.num=num
12. this.score=score
13. }
14. //继承
15. Student.prototype=new Person("小红",10)
16. Student.prototype.constructor=Student
17.
18. var stu =new Student(2016002288,80)
19.
20. stu.eat()//Person 吃饭
3.组合继承
组合继承其实就是结合了上述的两种方法来实现继承,拥有两种方法的优点
1. function Person(name,age,sex){
2. this.name=name;
3. this.age=age;
4. this.sex=sex;
5. }
6. Person.prototype.sayHi=function(){
7. console.log("你好")
8. }
9.
10. function Student(name,age,sex,score){
11. //借用构造函数
12. Person.call(this,name,age,sex)
13. this.score=score
14. }
15.
16. // 改变了原型指向
17. Student.prototype=new Person();//不传值
18. Student.prototype.eat=function(){
19. console.log("吃东西");
20. }
21.
22. var stu=new Student("小黑",20,"男","100分")
23. console.log(stu.name,stu.age,stu.sex,stu.score);
24. stu.sayHi()//你好
25. stu.eat()//吃东西
4.拷贝继承
类似于复制,把一个对象中的属性和方法直接复制到另一个对象中
1. function Person(){
2. }
3.
4. Person.prototype.name="小红"
5. Person.prototype.age=18
6.
7. function Student(){
8. }
9.
10. var p=Person.prototype;
11. var s=Student.prototype;
12.
13. for(key in p){
14. s[key]=p[key]
15. }
16.
17. console.dir(Student)
console
每次都要for in 好累 , 可以进行优化封装一下
1. function extend(Child,Parent) {
2.
3. var p = Parent.prototype;
4. var c = Child.prototype;
5.
6. for (var i in p) {
7. c[i] = p[i];
8. }
9.
10. //这个属性直接指向父对象的prototype属性,可以直接调用父对象的方法,为了实现继承的完备性,纯属备用性质
11. c.par = p;
12.
13. }
- 直接继承prototype
优点 : 效率比较高
缺点 : 因为相当于是个传址过程 所以修改Student的属性 Person 的也会被更改
1. function Person(){};
2.
3. Person.prototype.name="小红";
4. Person.prototype.age=18;
5.
6. function Student(){};
7.
8. Student.prototype=Person.prototype;
9.
10. console.dir(Student);
11. console.dir(Person);
12. Student.prototype.age=25;
console
6.利用空对象作中介实现继承
用这种方式修改 Student 的prototype 不会影响到 Person的prototype
1. function Person(){};
2. Person.prototype.name="小红";
3. Person.prototype.age=11;
4.
5. function Student(){};
6. var F=function(){};
7. F.prototype=Person.prototype;
8.
9. Student.prototype=new F();
10. Student.prototype.constructor=Student;
11.
12. Student.prototype.age=25;
13.
14. console.dir(Person)
15. console.dir(Student)
console
封装一下
1. function extend(Child,Parent) {
2.
3. var F = function(){};
4.
5. F.prototype = Parent.prototype;
6.
7. Child.prototype = new F();
8.
9. Child.prototype.constructor = Child;
10.
11. Child.par = Parent.prototype;
12.
13. }
(11)、web存储、cookies、localstroge、如何实现一个在一定时间后过期的localstorage、session和cookies的区别、cookies存储在哪?
基本概念
cookie:是网景公司的前雇员在1993年发明。它的主要用于保存登陆信息,比如登陆某个网站市场可以看到’记住密码’,这就是通过在cookie中存入一段辨别用户身份的数据来实现的。
sessionStorage:会话,是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但是页面关闭后,sessionStorage中的数据就会被清空。
localStorage:是HTML5标准中新加入的技术,当然早在IE6时代就有一个userData的东西用于本地存储,而当时考虑到浏览器的兼容性,更通用的方案是使用flash。如今localStorage被大多数浏览器所支持。
三者区别
1)存储大小
cookie:一般不超过4K(因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识)
sessionStorage:5M或者更大
localStorage:5M或者更大
2)数据有效期
cookie:一般由服务器生成,可以设置失效时间;若没有设置时间,关闭浏览器cookie失效,若设置了时间,cookie就会存放在硬盘里,过期才失效
sessionStorage:仅在当前浏览器窗口关闭之前有效,关闭页面或者浏览器会被清除
localStorage:永久有效,窗口或者浏览器关闭也会一直保存,除非手动永久清除,因此用作持久数据
3)作用域
cookie:在所有同源窗口中都是共享的
sessionStorage:在同一个浏览器窗口是共享的(不同浏览器、同一个页面也是不共享的)
localStorage:在所有同源窗口中都是共享的
4)通信
ccokie:十种携带在同源的http请求中,即使不需要,故cookie在浏览器和服务器之间来回传递;如果使用cookie保存过多数据会造成性能问题
sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信;不会自动把数据发送给服务器,仅在本地保存
localStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信;不会自动把数据发送给服务器,仅在本地保存
5)易用性
cookie:需要自己进行封装,原生的cookie接口不够友好
sessionStorage:原生接口可以接受,可以封装来对Object和Array有更好的支持
localStorage:原生接口可以接受,可以封装来对Object和Array有更好的支持
应用场景
cookie:判断用户是否登录过网站,以便实现下次自动登录或记住密码;保存事件信息等
sessionStorage:敏感账号一次性登录;单页面用的较多(sessionStorage 可以保证打开页面时 sessionStorage 的数据为空)
localStorage:常用于长期登录(判断用户是否已登录),适合长期保存在本地的数据
(12)、let const的优点
一、let和var
在开发中不要使用var,需要用变量时使用let。因为var是没有块级作用域(if和for)的,Es5中只有function有作用域,而let是有作用域的(if和for)。
二、关于const
1.const表示常量,一旦被const修饰的标识符不能修改。
2.用const定义标识符时,必须赋值。
3.常量表示被指向的对象不能修改,但其内部的属性可以修改。
建议:在Es6开发中,一般使用const,只有在标识符需要改变时使用let。
(13)、如何自己实现一个promise
实现代码:
var PENDING = 'PENDING'
var FULFILLED = 'FULFILLED'
var REJECTED = 'REJECTED'
var i = 1
// 1. 构造函数Promise
function Promise(fn) {
this.id = i++;
// 2. 构造函数内初始状态Pending和value
this.status = PENDING; // 初始化状态
this.value = null;// 初始化值
this.deffered = []; // 下一个执行的Promise是谁,子Promise
// 3. 构造函数内调用函数(apply参数是数组,call参数是一个一个的,调用函数改变this的指向)
// resolve和reject的this都是当前的Promise对象。 使用bind方法不会立即执行函数,而是返回一个新的函数!!!
fn.call(this, this.resolve.bind(this), this.reject.bind(this) );
}
// 4. 结束回调函数,执行then Promise.prototype.then是函数
// 5.then函数内需要保存起结果或者失败的函数
Promise.prototype = {
constructor: Promise,
resolve: function (data) {
this.status = FULFILLED;
this.value = data;
//执行后续行为
this.done()
},
reject: function (err) {
this.status = REJECTED;
this.value = err;
//执行后续行为
this.done()
},
done: function () {
// 让这些this.deffered(子Promise执行)
this.deffered.forEach(task => this.handler(task));
},
handler: function (task) {
// 判断当前的执行的状态是咋样,调用对应的函数
var status = this.status;
var value = this.value;
var p ;
switch (status) {
case FULFILLED:
p = task.onfulfilled(value)
break;
case REJECTED:
p = task.onrejected(value)
break;
}
// 如果 p 是一个Promise的话,我们需要让他继续执行
// 把后续(task.promise)的deffer交给这个p
if(p && p.constructor === Promise){
// 是下一个promise
// 把下一个作为then链接的deffer移交p的deffered
p.deffered = task.promise.deffered;
}
},
then: function (onfulfilled, onrejected) {
// 保存该函数
var obj = {
onfulfilled: onfulfilled,
onrejected: onrejected
}
// 新来一个Promise 对象,让其存储这些
// 并且能根据不同的Promise去then
obj.promise = new this.constructor(function () {});
console.log(this); // 1
console.log(obj.promise); // 2
//保存接下来的子Promise
//建立一个与下一个Promise之间的关系
if(this.status == PENDING) this.deffered.push(obj)
// 保证不报错,未来不能return自己,需要换人
return obj.promise;
}
}
难点:
then方法需要返回一个新的子Promise, 并且前后的Promise需要建立联系,才能决定他们的执行顺序。这里用id标识每个promise。
(14)、JQ源码的一些问题
1)为什么$(selector)之后,返回的是jQuery对象?
答:从jQuery的源代码中,我们可以知道:var $ = jQuery 。因此当我们$(selector)操作时,其实就是jQuery(selector),创建的是一个jQuery对象。当然正确的写法应该是这样的:var jq = new $(selector); 而jQuery使用了一个小技巧在外部避免了new,在jquery方法内部:
var jQuery = function(){
return new jQuery.prototype.init();
}
2)为什么创建一个jQuery对象之后,我们可以这样写$(selector).each(function(index){…});
进行遍历操作呢?
答:其实jQuery(selector)方法调用时,在jQuery(selector)方法内部,最后返回的是一个数组:return this.setArray(a); 而each方法体内部是一个for循环,在循环体内是这样调用的:method.call(this[i],i) 。
3)为什么jQuery能做到jQuery对象属性/方法/事件的插件式扩展?
答:如果您有一些javasciprt的面向对象方面的知识,就会知道,在jQuery.prototype原型对象上的扩展属性/方法和事件,将会给jQuery的对象“扩展”。基于这一点,jQuery是这样写的:jQuery.fn = jQuery.prototype
(15)、JS如何实现重载和多态
一、根据arguments个数实现重载
js本身不支持重载,所以只能通过其他方式实现,arguments检测传参的个数,然后再执行不同的方式
1. function add() {
2. var sum = 0 ;
3. for ( var i = 0 ; i < arguments.length; i ++ ) {
4. sum += arguments[i];
5. }
6. return sum;
7. }
8. alert(add());
9. alert(add( 1 , 2 ));
10. alert(add( 1 , 2 , 3 ));
1. function overLoading() {
2. // 根据arguments.length,对不同的值进行不同的操作
3. switch(arguments.length) {
4. case 0:
5. /*操作1的代码写在这里*/
6. break;
7. case 1:
8. /*操作2的代码写在这里*/
9. break;
10. case 2:
11. /*操作3的代码写在这里*/
12.
13. //后面还有很多的case......
14. }
15.
16. }
二、检测数据类型实现重载
根据传参的类型,调用不同的方法,用typeof进行检测
```javascript
1. //检测数据类型实现重载 typeof
2. var MyClass=function(){
3. var AddNum=function(a,b){
4. return a+b;
5. }
6. var AddString=function(a,b){
7. return "I am here"+a+b;
8. }
9. this.Add=function(a,b){
10. if(typeof(a)=="number")
11. return AddNum(a,b);
12. else
13. return AddString(a,b);
14. }
15. }
16.
17. function add(a,b){
18. return a+b;
19. }
20.
21. function add(a,b){
22. return "I am here"+a+b;
23. }
24.
25. var MyObj = new MyClass();
26. var X = MyObj.Add(5,6);
27. var Y = MyObj.Add("A","FFFFFF");
28. alert(X); //结果:11
29. alert(Y); //结果:I am hereAFFFFFF
三、jquery中的重载
如果传递2个参数表示获取值,传递三个参数表示赋值
```javascript
1. function attr(id, key, value){
2. var dom = $$.$id(id);
3. var args = arguments.length;
4. if(args ===3 ){
5. dom.setAttribute(key, value);
6. }else{
7. return dom.getAttribute(key);
8. }
9. }
10. attr(1,2);
11. attr(1,2,3);
js中实现多态
非多态代码示例
1. var makeSound = function(animal) {
2. if(animal instanceof Duck) {
3. console.log('嘎嘎嘎');
4. } else if (animal instanceof Chicken) {
5. console.log('咯咯咯');
6. }
7. }
8. var Duck = function(){}
9. var Chiken = function() {};
10. makeSound(new Chicken());
11. makeSound(new Duck());
多态的代码示例
1. var makeSound = function(animal) {
2. animal.sound();
3. }
4.
5. var Duck = function(){}
6. Duck.prototype.sound = function() {
7. console.log('嘎嘎嘎')
8. }
9. var Chiken = function() {};
10. Chiken.prototype.sound = function() {
11. console.log('咯咯咯')
12. }
13.
14. makeSound(new Chicken());
15. makeSound(new Duck());
(16)、js的基本类型有哪些?引用类型有哪些?
- 基本类型: string,number,boolean,null,undefined
- 引用类型: Function,Array,Object------技术对象系列,typeof()这个三种类型得到的都是object
(17)、null和undefined的区别
null表示"没有对象",即该处不应该有值。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。
(18)、Object是引用类型嘛?引用类型和基本类型有什么区别?哪个是存在堆哪一个是存在栈上面的?
基本类型:Number,String,Boolean,Null,undefined。
引用类型:Object,Array,Date,RegExp,Function
访问方式的不同
访问方式
基本类型 引用类型
操作和保存在变量的实际的值 值保存在内存中,js不允许直接访问内存,在操作的时候,操作的是对象的引用
存储位置的不同
存储的位置
基本类型 引用类型
保存在栈区
引用存放在栈区,实际对象保存在堆区
1.引用类型总是被分配到“堆”上。
2.值类型总是分配到它声明的地方:
a.作为引用类型的成员变量分配到“堆”上
b.作为方法的局部变量时分配到“栈”上
(19)、js的继承有哪些?分别列出他们的特点?
1、原型链继承
function Parent () { this.name = 'kevin';}
Parent.prototype.getName = function () { console.log(this.name);}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()) // kevin
优点:
1)、避免了引用类型的属性被所有实例共享
2)、可以在 Child 中向 Parent 传参
举个例子:
function Parent (name) { this.name = name;}
function Child (name) { Parent.call(this, name);}
var child1 = new Child('kevin');
console.log(child1.name); // kevin
var child2 = new Child('daisy');
console.log(child2.name); // daisy
缺点:
方法都在构造函数中定义,每次创建实例都会创建一遍方法。
3、组合继承
原型链继承和经典继承双剑合璧。
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green'];}
Parent.prototype.getName = function () { console.log(this.name)}
function Child (name, age) {
Parent.call(this, name); this.age = age;
}
Child.prototype = new Parent();Child.prototype.constructor = Child;
var child1 = new Child('kevin', '18');
child1.colors.push('black');
console.log(child1.name); // kevinconsole.log(child1.age); // 18console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisyconsole.log(child2.age); // 20console.log(child2.colors); // ["red", "blue", "green"]
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
4、原型式继承
function createObj(o) { function F(){} F.prototype = o; return new F();}
就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。
缺点:
包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
var person = { name: ‘kevin’, friends: [‘daisy’, ‘kelly’]}
var person1 = createObj(person);var person2 = createObj(person);
person1.name = ‘person1’;console.log(person2.name); // kevin
person1.firends.push(‘taylor’);console.log(person2.friends); // [“daisy”, “kelly”, “taylor”]
注意:修改person1.name的值,person2.name的值并未发生改变,并不是因为person1和person2有独立的 name 值,而是因为person1.name = ‘person1’,给person1添加了 name 值,并非修改了原型上的 name 值。
5、寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log('hi');
}
return clone;}
缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
6、寄生组合式继承
为了方便大家阅读,在这里重复一下组合继承的代码:
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green'];}
Parent.prototype.getName = function () { console.log(this.name)}
function Child (name, age) { Parent.call(this, name); this.age = age;}
Child.prototype = new Parent();
var child1 = new Child('kevin', '18');
console.log(child1)
(20)、原型链的概念?原型和原型链的区别?
1、什么是原型对象prototype?
原型是一个对象,并且只有函数有prototype。
prototype对象中有一个constructor属性,指向了这个函数本身。
function test () {}
var fn = new test() // test.prototype就是fn的原型对象,可以理解通过new 将 fn 和 test.prototype建立连接(原型继承)
console.log(test.prototype.constructor === test) // true
2、什么是__proto__
(隐式原型)?
_proto_实际上是某个实例对象的隐藏属性, 任何对象都有__proto__
,指向它所对应的构造函数的原型对象
我到这个地方就有点乱了,什么关系指来指去,为什么定义的字符串/number/true、false变量都有__proto__
。一脸懵逼啊!!!到这里就要岔开话题了,先了解下另外一个知识点,
js的三大包装对象Number()、String()、Boolean()
我们知道常用的 基本类型(原始类型):有 number、string、boolean、undefined、null
其中, number、string、boolean这三个类型比较特殊。在调用其某个方法时,JavaScript 引擎会自动将原始类型的值转为包装对象实例,也就是原始类型的“包装对象”。可以调用各种包装对象的属性和方法,在使用后立刻销毁实例。自动转换生成的包装对象是只读的,无法修改。
let str = 'hello word'
console.log(str.length)
// 访问length属性,但str本身不是对象,不能调用length属性。
// javascript引擎自动将其转为String包装对象,在这个对象上调用length属性。调用结束后,这个临时对象就会被销毁。
// number、boolean类型等同
复杂类型: array、function、object
复杂类型是由js内置对应的构造器实例而来,Array、Function、Object
回归主题,__proto__指向它所对应的构造函数的原型对象
所以这里,第一步就很清晰了
let str = 'hello word'
console.log(str.__proto__ === String.prototype) // true
let num = 1
console.log(num.__proto__ === Number.prototype) // true
let flag = true
console.log(flag.__proto__ === Boolean.prototype) // true
function test () {}
console.log(test.__proto__ === Function.prototype) // true
let arr = []
console.log(arr.__proto__ === Array.prototype) // true
let obj = {}
console.log(obj.__proto__ === Object.prototype) // true
所有的构造器都是Function函数(构造器)的实例,所以以上所有构造器(String、Number、Boolean、Function、Array、Object)的__proto__
,均指向Function.prototype
3、什么是原型链?
以上对原型对象和__proto__有了一定的了解,那什么是原型链?
当访问一个对象的某个属性时,会先在这个对象本身的属性上找,如果没有找到,会去这个属性的__proto__属性上找,即这个构造函数的prototype,如果还没找到,就会继续在__proto__上查找,直到最后一层,找不到即为undefined。这样一层一层往上找,彷佛是一条链子串起来,所以叫做原型链
(21)、es6的继承和es5的继承有什么区别?
- 类的内部定义的所有方法,都是不可枚举的。
2.ES6的class类必须用new命令操作,而ES5的构造函数不用new也可以执行。
3.ES6的class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
4.ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
(22)、vue1和vue2的区别?
1、生命周期函数
(1)vue1.0
周期 解释
init 组件刚刚被创建,但Data、method等属性还没被计算出来
created 组件创建已经完成,但DOM还没被生成出来
beforeCompile 模板编译之前
compiled 模板编译之后
ready 组件准备(平时用得较多)
attached 在 vm.$el 插入到DOM时调用
detached 在 vm.$el 从 DOM 中删除时调用
beforeDestory 组件销毁之前
destoryed 组件销毁之后
(2)vue2.0
周期 解释
beforeCreate 组件刚刚被创建,但Data、method等属性还没被计算出来
created 组件创建已经完成,但DOM还没被生成出来
beforeMount 模板编译之前
mounted 模板编译之后,组件准备
beforeUpdate 组件更新之前(数据等变动的时候)
updated 组件更新之后(数据等变动的时候)
activated for keep-alive,组件被激活时调用
deactivated for keep-alive,组件被移除时调用
beforeDestory 组件销毁之前
destoryed 组件销毁之后
2.0生命生命周期变化感觉变得更加语义化一点(有规律可寻,更好记了),而且增加了beforeUpdate、updated、activated、deactivated,删除了attached、detached。
2、过滤器
(1)vue1.0
自带过滤器。
定义方式:vue.filter(‘过滤器名字’,fn)
调用方式:{{msg | filterName’12’ ‘5’}}
(2)vue2.0
2.0移除了自带过滤器,但是保留了自定义过滤器的功能。
定义方式:vue.filter(‘过滤器名字’,fn)
调用方式:{{msg | filterName(‘12’,‘5’)}}
以下是一个自定义过滤器示例:
Vue.filter('toDou',function(n,a,b){
return n<10?n+a+b:''+n;
});
3、循环
关于整数循环,1.0的整数循环是从0开始的,2.0的整数循环是从1开始的,下面对比:
//HTML代码
<ul id='box'>
<li v-for='val in 5' v-text='val'>
</li>
</ul>
4、代码片段
编写template的时候,2.0必须要用一个根元素(如div)将代码片段包裹起来,否则报错。
// 1.0
<template>
<h3>我是组件</h3><strong>我是加粗标签</strong>
</template>
// 2.0: 必须有根元素,包裹住所有的代码
<template id="aaa">
<div>
<h3>我是组件</h3>
<strong>我是加粗标签</strong>
</div>
</template>
5、组件定义
(1)vue1.0
1)定义组件的方式:
Vue.extend 这种方式,在2.0里面有,但是有一些改动
Vue.component(组件名称,{ 在2.0继续能用
data(){
}
methods:{
}
template:
});
2)局部注册
var Child = Vue.extend({ /* ... */ })
var Parent = Vue.extend({
template: '...',
components: { // <my-component> will only be available in Parent's template
'my-component': Child
}
})
(2)vue2.0
1)定义组件的方式:
var Home={ template:'' -> 相当于Vue.extend()
};
2)局部注册:
var Child = { template: '<div>A custom component!</div>'}new Vue({ // ...
components: { // <my-component> 将只在父模板可用
'my-component': Child
}
})
6、给元素付唯一值
(1)vue 1.0 trace-by的方式
<div v-for="item in items" track-by="$index">
(2)vue 2.0 key的方式
<div v-for="item in items" :key="item.id">
7、自定义键盘指令
(1)vue1.0
Vue.directive('on').keyCodes.f1=17
(2)vue2.0
Vue.config.keyCodes.ctrl=17