express&koa面试题目:1.express和koa的对比,两者中间件的原理,koa捕获异常多种情况说一下参考:https://blog.csdn.net/shmnh/a…https://blog.csdn.net/K616358...https://blog.csdn.net/wang839...async 函数:http://www.ruanyifeng.com/blo…初识两者express:var express = require(’express’)var app = express() //创建一个APP实例 //建一个项目根目录的get请求路由,回调方法中直接输出字符串Hello World!app.get(’/’, function (req, res) { res.send(‘Hello World!’)}); //监听端口,启动服务app.listen(3000);koa:var koa = require(‘koa’);var route = require(‘koa-route’); //koa默认没有集成route功能,引入中间件 var app = koa(); //创建一个APP实例 //建一个项目根目录的get请求路由,回调方法中直接输出字符串Hello World!,就是挂载一个中间件app.use(route.get(’/’, function *(){ this.body = ‘Hello World’;})); //监听端口,启动服务app.listen(3000);启动方式koa采用了new Koa()的方式,而express采用传统的函数形式,对比源码如下://koaconst Emitter = require(’events’);module.exports = class Application extends Emitter {…}//expressexports = module.exports = createApplication;function createApplication() {…}应用生命周期和上下文在项目过程中,经常需要用到在整个应用生命周期中共享的配置和数据对象,比如服务URL、是否启用某个功能特性、接口配置、当前登录用户数据等等。 express://共享配置,express提供了很多便利的方法app.set(’enableCache’, true)app.get(’enableCache’)//true app.disable(‘cache’)app.disabled(‘cache’)//true app.enable(‘cache’)app.enabled(‘cache’)//true //应用共享数据:app.localsapp.locals.user = {name:“Samoay”, id:1234};koa://配置,直接使用koa context即可app.enableCache = true; app.use(function (next){ console.log(this.app.enableCache); //true this.app.enableCache = false; //just use this this.staticPath = ‘static’; yield next;}); //应用共享数据:ctx.statethis.state.user = {name:“Samoay”, id:1234}; 请求HTTP Request服务器端需要进行什么处理,怎么处理以及处理的参数都依赖客户端发送的请求,两个框架都封装了HTTP Request对象,便于对这一部分进行处理。以下主要举例说明下对请求参数的处理。GET参数都可以直接通过Request对象获取,POST参数都需要引入中间件先parse,再取值。express:// 获取QueryString参数// GET /shoes?order=desc&shoe[color]=bluereq.query.order// => “desc” req.query.shoe.color// => “blue” // 通过路由获取Restful风格的URL参数app.get(’/user/:id?’, function userIdHandler(req, res) { console.log(req.params.id); res.send(‘GET’);}) //获取POST数据:需要body-parser中间件var bodyParser = require(‘body-parser’);app.use(bodyParser.urlencoded({ extended: true }));app.post(’/’, function (req, res) { console.log(req.body); res.json(req.body);koa:// 获取QueryString参数// GET /?action=delete&id=1234this.request.query// => { action: ‘delete’, id: ‘1234’ } // 通过路由获取Restful风格的URL参数var route = require(‘koa-route’);app.use(route.get(’/post/:id’, function (id){ console.log(id); // => 1234})); // 获取POST数据:需要co-body中间件// Content-Type: application/x-www-form-urlencoded// title=Test&content=This+is+a+test+postvar parse = require(‘co-body’);app.use(route.post(’/post/new’, function (){ var post = yield parse(this.request);//this console.log(post); // => { title: ‘Test’, content: ‘This is a test post’ }}));路由Route收到客户端的请求,服务需要通过识别请求的方法(HTTP Method: GET, POST, PUT…)和请求的具体路径(path)来进行不同的处理。这部分功能就是路由(Route)需要做的事情,说白了就是请求的分发,分发到不同的回调方法去处理。express// app.all表示对所有的路径和请求方式都要经过这些回调方法的处理,可以逗号方式传入多个app.all(’’, authentication, loadUser);// 也可以多次调用app.all(’’, requireAuthentication)app.all(’’, loadUser);// 也可以针对某具体路径下面的所有请求app.all(’/api/’, requireAuthentication); // app.get GET方式的请求app.get(’/user/:id’, function(req, res) { res.send(‘user ’ + req.params.id);}); // app.post POST方式的请求app.post(’/user/create’, function(req, res) { res.send(‘create new user’);});这里需要说明2个问题,首先是app.get,在应用生命周期中也有一个app.get方法,用于获取项目配置。Express内部就是公用的一个方法,如果传入的只有1个参数就获取配置,2个参数就作为路由处理。其次是app.use(’’, cb) 与app.all(’’, cb) 的区别,前者是中间件方式,调用是有顺序的,不一定会执行到;后者是路由方式,肯定会执行到。koa// Koa// 和Express不同,koa需要先引入route中间件var route = require(‘koa-route’); //引入中间件之后支持的写法差不多,只是路径传入route,然后把route作为中间件挂载到appapp.use(route.get(’/’, list));app.use(route.get(’/post/new’, add));app.use(route.get(’/post/:id’, show));app.use(route.post(’/post’, create)); //链式写法var router = require(‘koa-router’)(); router.get(’/’, list) .get(’/post/new’, add) .get(’/post/:id’, show) .post(’/post’, create); app.use(router.routes()) .use(router.allowedMethods());视图viewExpress框架自身集成了视图功能,提供了consolidate.js功能,可以是有几乎所有Javascript模板引擎,并提供了视图设置的便利方法。Koa需要引入co-views中间件,co-views也是基于consolidate.js,支持能力一样强大。express// Express// 这只模板路径和默认的模板后缀app.set(‘views’, __dirname + ‘/tpls’);app.set(‘view engine’, ‘html’); //默认,express根据template的后缀自动选择模板//引擎渲染,支持jade和ejs。如果不使用默认扩展名app.engine(ext, callback) app.engine(‘html’, require(’ejs’).renderFile); //如果模板引擎不支持(path, options, callback)var engines = require(‘consolidate’);app.engine(‘html’, engines.handlebars);app.engine(’tpl’, engines.underscore); app.get(’list’, function(res, req){ res.render(’list’, {data});});koa//需要引入co-views中间件var views = require(‘co-views’); var render = views(’tpls’, { map: { html: ‘swig’ },//html后缀使用引擎 default: “jade”//render不提供后缀名时}); var userInfo = { name: ’tobi’, species: ‘ferret’}; var html;html = render(‘user’, { user: userInfo });html = render(‘user.jade’, { user: userInfo });html = render(‘user.ejs’, { user: userInfo });返回HTTP Response获取完请求参数、处理好了具体的请求、视图也准备就绪,下面就该返回给客户端了,那就是HTTP Response对象了。这部分也属于框架的基础部分,各种都做了封装实现,显著的区别是koa直接将输出绑定到了ctx.body属性上,另外输出JSON或JSONP需要引入中间件。express//输出普通的htmlres.render(’tplName’, {data}); //输出JSONres.jsonp({ user: ‘Samoay’ });// => { “user”: “Samoay” } //输出JSONP ?callback=foores.jsonp({ user: ‘Samoay’ });// => foo({ “user”: “Samoay” }); //res.send([body]);res.send(new Buffer(‘whoop’));res.send({ some: ‘json’ });res.send(’<p>some html</p>’); //设定HTTP Status状态码res.status(200);koaapp.use(route.get(’/post/update/:id’, function *(id){ this.status = 404; this.body = ‘Page Not Found’;})); var views = require(‘co-views’);var render = views(’tpls’, { default: “jade”//render不提供后缀名时});app.use(route.get(’/post/:id’, function (id){ var post = getPost(id); this.status = 200;//by default, optional this.body = yield render(‘user’, post);})); //JSONvar json = require(‘koa-json’);app.use(route.get(’/post/:id’, function (id){ this.body = {id:1234, title:“Test post”, content:"…"};}));中间件 Middleware对比了主要的几个框架功能方面的使用,其实区别最大,使用方式最不同的地方是在中间件的处理上。Express由于是在ES6特性之前的,中间件的基础原理还是callback方式的;而koa得益于generator特性和co框架(co会把所有generator的返回封装成为Promise对象),使得中间件的编写更加优雅。express// req 用于获取请求信息, ServerRequest 的实例// res 用于响应处理结果, ServerResponse 的实例// next() 函数用于将当前控制权转交给下一步处理,// 如果给 next() 传递一个参数时,表示出错信息var x = function (req, res, next) { // 对req和res进行必要的处理 // 进入下一个中间件 return next(); // 传递错误信息到下一个中间件 return next(err); // 直接输出,不再进入后面的中间件 return res.send(‘show page’);};koa// koa 一切都在ctx对象上+generatorapp.use(function (){ this; // is the Context this.request; // is a koa Request this.response; // is a koa Response this.req;// is node js request this.res;// is node js response //不再进入后面的中间件, 回溯upstream return;});express处理多个中间件:const app = require(“express”)();app.use((req,res,next)=>{ console.log(“first”); //next();});app.use((req,res,next)=>{ console.log(“second”); //next();});app.use((req,res,next)=>{ console.log(“third”); res.status(200).send("<h1>headers …</h1>");});app.listen(3001);koa处理多个中间件:const Koa = require(‘koa’);const app = new Koa();app.use((ctx,next) => { ctx.body = ‘Hello Koa-1’; next(); }); app.use((ctx,next) => { ctx.body = ‘Hello Koa-2’; next(); }); app.use((ctx,next) => { ctx.body = ‘Hello Koa-3’; next(); });app.listen(3000);/与express类似,koa中间件的入参也有两个,后一个就是next。next的功能与express一样//上面介绍了koa的next()的功能,这里的next()需要同步调用,千万不要采用异步调用/koa捕获异常异常捕获 const http = require(‘http’);const https = require(‘https’);const Koa = require(‘koa’);const app = new Koa();app.use((ctx)=>{ str=“hello koa2”;//沒有声明变量 ctx.body=str;})app.on(“error”,(err,ctx)=>{//捕获异常记录错误日志 console.log(new Date(),":",err);});http.createServer(app.callback()).listen(3000);上面的代码运行后在浏览器访问返回的结果是“Internal Server error”;我们发现当错误发生的时候后端程序并没有死掉,只是抛出了异常,前端也同时接收到了错误反馈,对于KOA来说,异常发生在中间件的执行过程中,所以只要我们在中间件执行过程中将异常捕获并处理就OK了。 添加中间键use方法use(fn) { if (typeof fn !== ‘function’) throw new TypeError(‘middleware must be a function!’); if (isGeneratorFunction(fn)) { deprecate(‘Support for generators will be removed in v3. ’ + ‘See the documentation for examples of how to convert old middleware ’ + ‘https://github.com/koajs/koa/blob/master/docs/migration.md'); fn = convert(fn); } debug(‘use %s’, fn._name || fn.name || ‘-’); this.middleware.push(fn); return this; }/fn可以是三种类型的函数,普通函数,generator函数,还有async函数。最后generator会被转成async函数,。所以最终中间件数组只会有普通函数和async函数。/异常处理当异常捕获是有两种处理方式,一种就是响应错误请求,而就是触发注册注册全局错误事件,比如记录错误日志async 函数一句话,async 函数就是 Generator 函数的语法糖。前文有一个 Generator 函数,依次读取两个文件:var fs = require(‘fs’);var readFile = function (fileName){ return new Promise(function (resolve, reject){ fs.readFile(fileName, function(error, data){ if (error) reject(error); resolve(data); }); });};var gen = function (){ var f1 = yield readFile(’/etc/fstab’); var f2 = yield readFile(’/etc/shells’); console.log(f1.toString()); console.log(f2.toString());};写成 async 函数,就是下面这样:var asyncReadFile = async function (){ var f1 = await readFile(’/etc/fstab’); var f2 = await readFile(’/etc/shells’); console.log(f1.toString()); console.log(f2.toString());};一比较就会发现,async 函数就是将 Generator 函数的星号()替换成 async,将 yield 替换成 await,仅此而已。async 函数的优点(1)内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。var result = asyncReadFile();(2)更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。(3)更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。async 函数的实现async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。async function fn(args){ // …}// 等同于function fn(args){ return spawn(function() { // … }); }所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。async 函数的用法同 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);});上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。指定多少毫秒后输出一个值:function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); });}async function asyncPrint(value, ms) { await timeout(ms); console.log(value)}asyncPrint(‘hello world’, 50);await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); }}// 另一种写法async function myFunction() { await somethingThatReturnsAPromise().catch(function (err){ console.log(err); });}await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。但是,如果将 forEach 方法的参数改成 async 函数,也有问题。async function dbFuc(db) { let docs = [{}, {}, {}]; // 可能得到错误结果 docs.forEach(async function (doc) { await db.post(doc); });}//上面代码可能不会正常工作,原因是这时三个 db.post 操作将是并发执行,//也就是同时执行,而不是继发执行。正确的写法是采用 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);}promise: https://segmentfault.com/n/13...JS的继承面试题目:9.js的继承参考:http://www.ruanyifeng.com/blo…构造函数的继承例子:function Animal(){ this.species = “动物”; }function Cat(name,color){ this.name = name; this.color = color; }一、 构造函数绑定第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat(“大毛”,“黄色”); alert(cat1.species); // 动物二、 prototype模式如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了//将Cat的prototype对象指向一个Animal的实例//它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。 Cat.prototype = new Animal(); //任何一个prototype对象都有一个constructor属性,指向它的构造函数。//如果没有"Cat.prototype = new Animal();//“这一行,Cat.prototype.constructor是指向Cat的;//加了这一行以后,Cat.prototype.constructor指向Animal。 Cat.prototype.constructor = Cat; var cat1 = new Cat(“大毛”,“黄色”); alert(cat1.species); // 动物 alert(Cat.prototype.constructor == Animal); //true //每一个实例也有一个constructor属性, //默认调用prototype对象的constructor属性。 alert(cat1.constructor == Cat.prototype.constructor); // true //在运行"Cat.prototype = new Animal();“这一行之后, //cat1.constructor也指向Animal! alert(cat1.constructor == Animal); // true //这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须 //手动纠正,将Cat.prototype对象的constructor值改为Cat。 //这就是第二行的意思。这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。o.prototype = {};o.prototype.constructor = o;三、 直接继承prototype由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。先将Animal对象改写:function Animal(){ }Animal.prototype.species = “动物”;然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat(“大毛”,“黄色”); alert(cat1.species); // 动物这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。Cat.prototype.constructor = Cat;// 这一句实际上把Animal.prototype对象的constructor属性也改掉了!alert(Animal.prototype.constructor); // Cat四、 利用空对象作为中介var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。alert(Animal.prototype.constructor); // Animal将上面的方法,封装成一个函数,便于使用。function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; }//意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。//(uber是一个德语词,意思是"向上”、“上一层”。)这等于在子对象上打开一条通道,//可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。使用的时候,方法如下extend(Cat,Animal);var cat1 = new Cat(“大毛”,“黄色”);alert(cat1.species); // 动物五、 拷贝继承上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,把父对象的所有属性和方法,拷贝进子对象 function Animal(){} Animal.prototype.species = “动物”;实现属性拷贝的目的:function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; //这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child //对象的prototype对象。 }使用的时候,这样写:extend2(Cat, Animal); var cat1 = new Cat(“大毛”,“黄色”); alert(cat1.species); // 动物非构造函数的继承例子:var Chinese = { nation:‘中国’ }; var Doctor ={ career:‘医生’ }这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现"继承”。object()方法 function object(o) { function F() {} F.prototype = o; return new F(); }//这个object()函数,其实只做一件事,就是把子对象的prototype属性,//指向父对象,从而使得子对象与父对象连在一起。使用的时候,第一步先在父对象的基础上,生成子对象:var Doctor = object(Chinese);然后,再加上子对象本身的属性:Doctor.career = ‘医生’;这时,子对象已经继承了父对象的属性了 alert(Doctor.nation); //中国浅拷贝除了使用"prototype链"以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } c.uber = p; return c; }使用的时候,这样写:var Doctor = extendCopy(Chinese);Doctor.career = ‘医生’;alert(Doctor.nation); // 中国但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。//现在给Chinese添加一个"出生地"属性,它的值是一个数组。 Chinese.birthPlaces = [‘北京’,‘上海’,‘香港’];//然后,我们为Doctor的"出生地"添加一个城市: Doctor.birthPlaces.push(‘厦门’);//Chinese的"出生地"也被改掉了alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做"浅拷贝"。这是早期jQuery实现继承的方式。深拷贝所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。function deepCopy(p, c) { var c = c || {}; for (var i in p) { if (typeof p[i] === ‘object’) { c[i] = (p[i].constructor === Array) ? [] : {}; deepCopy(p[i], c[i]); } else { c[i] = p[i]; } } return c; }使用的时候这样写:var Doctor = deepCopy(Chinese,Doctor);现在,给父对象加一个属性,值为数组。然后,在子对象上修改这个属性 Chinese.birthPlaces = [‘北京’,‘上海’,‘香港’]; Doctor.birthPlaces.push(‘厦门’); alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门 alert(Chinese.birthPlaces); //北京, 上海, 香港call和apply的区别面试题:10.call和apply的区别参考: http://www.ruanyifeng.com/blo...https://www.jianshu.com/p/bc5...this 用法this是 JavaScript 语言的一个关键字。它是函数运行时,在函数体内部自动生成的一个对象,只能在函数体内部使用。情况一:纯粹的函数调用这是函数的最通常用法,属于全局性调用,因此this就代表全局对象。var x = 1;function test() { console.log(this.x);}test(); // 1情况二:作为对象方法的调用函数还可以作为某个对象的方法调用,这时this就指这个上级对象。function test() { console.log(this.x);}var obj = {};obj.x = 1;obj.m = test;obj.m(); // 1情况三 作为构造函数调用所谓构造函数,就是通过这个函数,可以生成一个新对象。这时,this就指这个新对象。function test() { this.x = 1;}var obj = new test();obj.x // 1//为了表明这时this不是全局对象,我们对代码做一些改变var x = 2;function test() { this.x = 1;}var obj = new test();x // 2//运行结果为2,表明全局变量x的值根本没变。情况四 apply 调用apply()是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this指的就是这第一个参数。var x = 0;function test() { console.log(this.x);}var obj = {};obj.x = 1;obj.m = test;obj.m.apply() // 0//如果把最后一行代码修改为obj.m.apply(obj); //1callcall 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。var arr = [1, 2, 3, 89, 46]var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4])//89例子:var obj = { message: ‘My name is: ‘}function getName(firstName, lastName) { console.log(this.message + firstName + ’ ’ + lastName)}getName.call(obj, ‘Dot’, ‘Dolby’)applyapply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。var arr = [1,2,3,89,46]var max = Math.max.apply(null,arr)//89当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。例子:var obj = { message: ‘My name is: ‘}function getName(firstName, lastName) { console.log(this.message + firstName + ’ ’ + lastName)}getName.apply(obj, [‘Dot’, ‘Dolby’])// My name is: Dot Dolbycall和apply可用来借用别的对象的方法,这里以call()为例:var Person1 = function () { this.name = ‘Dot’;}var Person2 = function () { this.getname = function () { console.log(this.name); } Person1.call(this);}var person = new Person2();person.getname(); // Dotbind和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。var obj = { name: ‘Dot’}function printName() { console.log(this.name)}var dot = printName.bind(obj)console.log(dot) // function () { … }dot() // Dot//bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。 //而原函数printName 中的 this 并没有被改变,依旧指向全局对象 window。参数的使用function fn(a, b, c) { console.log(a, b, c);}var fn1 = fn.bind(null, ‘Dot’);fn(‘A’, ‘B’, ‘C’); // A B Cfn1(‘A’, ‘B’, ‘C’); // Dot A Bfn1(‘B’, ‘C’); // Dot B Cfn.call(null, ‘Dot’); // Dot undefined undefined//call 是把第二个及以后的参数作为 fn 方法的实参传进去,//而 fn1 方法的实参实则是在 bind 中参数的基础上再往后排。应用场景求数组中的最大和最小值var arr = [1,2,3,89,46]var max = Math.max.apply(null,arr)//89var min = Math.min.apply(null,arr)//1将类数组转化为数组var trueArr = Array.prototype.slice.call(arrayLike)数组追加var arr1 = [1,2,3];var arr2 = [4,5,6];var total = [].push.apply(arr1, arr2);//6// arr1 [1, 2, 3, 4, 5, 6]// arr2 [4,5,6]判断变量类型function isArray(obj){ return Object.prototype.toString.call(obj) == ‘[object Array]’;}isArray([]) // trueisArray(‘dot’) // false利用call和apply做继承function Person(name,age){ // 这里的this都指向实例 this.name = name this.age = age this.sayAge = function(){ console.log(this.age) }}function Female(){ Person.apply(this,arguments)//将父元素所有方法在这里执行一遍就继承了}var dot = new Female(‘Dot’,2)使用 log 代理 console.logfunction log(){ console.log.apply(console, arguments);}// 当然也有更方便的 var log = console.log()call、apply和bind函数存在的区别bind返回对应函数, 便于稍后调用; apply, call则是立即调用。除此外, 在 ES6 的箭头函数下, call 和 apply 将失效, 对于箭头函数来说:箭头函数体内的 this 对象, 就是定义时所在的对象, 而不是使用时所在的对象;所以不需要类似于var _this = this这种丑陋的写法箭头函数不可以当作构造函数,也就是说不可以使用 new 命令, 否则会抛出一个错误箭头函数不可以使用 arguments 对象,,该对象在函数体内不存在. 如果要用, 可以用 Rest 参数代替不可以使用 yield 命令, 因此箭头函数不能用作 Generator 函数ajax面试题: 11.ajax是同步还是异步,怎么样实现同步;12.ajax实现过程参考: https://blog.csdn.net/qq_2956...https://blog.csdn.net/xxf1597...Ajax全称Asynchronous JavaScript and XML,也就是异步的js和XML技术。Ajax的使用四大步骤详解第一步,创建xmlhttprequest对象var xmlhttp =new XMLHttpRequest();//XMLHttpRequest对象用来和服务器交换数据。var xhttp;if(window.XMLHttpRequest) {//现代主流浏览器xhttp= new XMLHttpRequest();}else{//针对浏览器,比如IE5或IE6xhttp= new ActiveXObject(“Microsoft.XMLHTTP”);}第二步,使用xmlhttprequest对象的open()和send()方法发送资源请求给服务器。xmlhttp.open(method,url,async) method包括get 和post,url主要是文件或资源的路径,async参数为true(代表异步)或者false(代表同步)xhttp.send();使用get方法发送请求到服务器。xhttp.send(string);使用post方法发送请求到服务器。post 发送请求什么时候能够使用呢?(1)更新一个文件或者数据库的时候。(2)发送大量数据到服务器,因为post请求没有字符限制。(3)发送用户输入的加密数据。get例子:xhttp.open(“GET”,“ajax_info.txt”,true);xhttp.open(“GET”,“index.html”,true);xhttp.open(“GET”,“demo_get.asp?t="+ Math.random(), true);xhttp.send();post例子xhttp.open(“POST”, “demo_post.asp”, true);xhttp.send();post表单例子post表单数据需要使用xmlhttprequest对象的setRequestHeader方法增加一个HTTP头。xhttp.open(“POST”,“ajax_test.aspx”,true);xhttp.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);xhttp.send(“fname=Henry&lname=Ford”);async=true 当服务器准备响应时将执行onreadystatechange函数。xhttp.onreadystatechange= function(){if(xhttp.readyState == 4 && xhttp.status == 200) { document.getElementById(“demo”).innerHTML=xhttp.responseText;}};xhttp.open(“GET”, “index.aspx”,true);xhttp.send();asyn=false 则将不需要写onreadystatechange函数,直接在send后面写上执行代码。xhttp.open(“GET”, “index.aspx”, false);xhttp.send();document.getElementById(“demo”).innerHTML = xhttp.responseText;第三步,使用xmlhttprequest对象的responseText或responseXML属性获得服务器的响应。使用responseText属性得到服务器响应的字符串数据,使用responseXML属性得到服务器响应的XML数据。例子如下:document.getElementById(“demo”).innerHTML = xhttp.responseText;//服务器响应的XML数据需要使用XML对象进行转换。xmlDoc= xhttp.responseXML;txt= “";x= xmlDoc.getElementsByTagName(“ARTIST”);for(i = 0; i < x.length; i++) {txt+= x[i].childNodes[0].nodeValue + “<br>”;}document.getElementById(“demo”).innerHTML= txt;第四步,onreadystatechange函数当发送请求到服务器,我们想要服务器响应执行一些功能就需要使用onreadystatechange函数,每次xmlhttprequest对象的readyState发生改变都会触发onreadystatechange函数。onreadystatechange属性存储一个当readyState发生改变时自动被调用的函数。readyState属性,XMLHttpRequest对象的状态,改变从0到4,0代表请求未被初始化,1代表服务器连接成功,2请求被服务器接收,3处理请求,4请求完成并且响应准备。status属性,200表示成功响应,404表示页面不存在。在onreadystatechange事件中,服务器响应准备的时候发生,当readyState==4和status==200的时候服务器响应准备。步骤总结创建XMLHttpRequest对象,也就是创建一个异步调用对象.创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息. 设置响应HTTP请求状态变化的函数. 发送HTTP请求. 获取异步调用返回的数据. 使用JavaScript和DOM实现局部刷新.同步&异步AJAX中根据async的值不同分为同步(async = false)和异步(async = true)两种执行方式$.ajax({ type: “post”, url: “path”, cache:false, async:false, dataType: ($.browser.msie) ? “text” : “xml”, success: function(xmlobj){ function1(){}; } }); function2(){};一.什么是同步请求:(false)同步请求即是当前发出请求后,浏览器什么都不能做,必须得等到请求完成返回数据之后,才会执行后续的代码,相当于是排队,前一个人办理完自己的事务,下一个人才能接着办。也就是说,当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面处于一个假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面解除假死状态(即当ajax返回数据后,才执行后面的function2)。 二.什么是异步请求:(true) 异步请求就当发出请求的同时,浏览器可以继续做任何事,Ajax发送请求并不会影响页面的加载与用户的操作,相当于是在两条线上,各走各的,互不影响。一般默认值为true,异步。异步请求可以完全不影响用户的体验效果,无论请求的时间长或者短,用户都在专心的操作页面的其他内容,并不会有等待的感觉。同步适用于一些什么情况呢? 我们可以想一下,同步是一步一步来操作,等待请求返回的数据,再执行下一步,那么一定会有一些情况,只有这一步执行完,拿到数据,通过获取到这一步的数据来执行下一步的操作。这是异步没有办法实现的,因此同步的存在一定有他存在的道理。我们在发送AJAX请求后,还需要继续处理服务器的响应结果,如果这时我们使用异步请求模式同时未将结果的处理交由另一个JS函数进行处理。这时就有可能发生这种情况:异步请求的响应还没有到达,函数已经执行完了return语句了,这时将导致return的结果为空字符串。闭包面试题:13.闭包的作用理解,以及那些地方用过闭包,以及闭包的缺点,如何实现闭包标题文字