阿里巴巴前端面试题(一)

(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")
  1. 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.	  }
  1. 直接继承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的基本类型有哪些?引用类型有哪些?

  1. 基本类型: string,number,boolean,null,undefined
  2. 引用类型: 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的继承有什么区别?

  1. 类的内部定义的所有方法,都是不可枚举的。
    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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值