面试问题解答
原型与原型链的了解?
原型是一个可以被复制(或者叫克隆)的一个类,通过复制原型可以创建一个一模一样的新对象。通俗的说,原型就是一个模板,在设计语言中更准确的说是一个对象模板。
原型(Person)定义了一些公用的属性和方法;利用原型(Person)创建出来的新对象实例(joe和john对象)会共享原型(Person)的所有属性和方法。
函数也是一个对象,对象不一定是函数。(对象有__proto__属性,函数有prototype属性)此处说明,方便大家理解下文。在javascript中,所有的对象都拥有一个__proto__属性指向该对象的原型(prototype)。
构造函数:
- 构造函数也是一个普通函数,创建方式和普通函数一样,但构造函数习惯上首字母大写
- 构造函数和普通函数的区别在于:调用方式不一样。作用也不一样(构造函数用来新建实例对象)
- 3、调用方式不一样。
- a. 普通函数的调用方式:直接调用 person();
- 构造函数的调用方式:需要使用new关键字来调用 new Person();
- 内部用this 来构造属性和方法
同步与异步?
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
:普通B/S模式(同步)AJAX技术(异步)
同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
同步、异步、阻塞和非阻塞的概念
在进行网络编程时,我们常常见到同步、异步、阻塞和非阻塞四种调用方式。这些方式彼此概念并不好理解。下面是我对这些术语的理解。
- 同步
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。最常见的例子就是 SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。
- 异步
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。以CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行。当连接真正建立起来以后,socket底层会发送一个消息通知该对象。这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误)。如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
- 阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。socket接收数据的另外一个函数recv则是一个阻塞调用的例子。当socket工作在阻塞模式的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
- 非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
对象的阻塞模式和阻塞函数调用
对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。
单线程与多线程?
什么是进程?
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
而一个进程又是由多个线程所组成的。
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,
即不同的线程可以执行同样的函数。
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,
也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
多线程的好处:
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,
这样就大大提高了程序的效率。
坏处:增加了调度和管理的开销,带来了一些不确定性,需要复杂的同步机制,避免死锁等等。
好处:一定程度上提高响应速度,在多核的情况下还是更能充分利用CPU资源的。
单线程,多线程,同步,异步
js就是单线程应用
线程是由cpu执行的最小单元
就好比一件事情可以分给几个人做
效率得到提高
单线程为了弥补这个缺点,就使用异步来解决
异步其实和回调很像,对于js来程序,如果执行耗时逻辑,肯定会阻塞当前线程,那要解决这个问题就使用异步
同步正好相反,会阻塞当前线程,直到响应后才能继续执行
多线程用途很广,比如你要从服务器获取一个很大的文件
如果是单线程,那就会很慢。
就会用异步的方式,等待获取数据后再处理
如果用同步就会阻塞
并行是针对多核心cpu,它才可以真正的并行。也就是cpu可以同时处理。就好比你有几个脑子,可以分别思考不同的事情
并发是cpu利用时间分片处理事情,比如你同时可能洗衣服,吃饭,看电视,本质上肯定做不到同时。但是你可以10毫秒洗衣服,10毫秒看电视。如果切换速度够快,那就看起来是同时执行了
多线程就是并发,利用cpu时间分片处理的
Java就是多线程的,js没有多线程,用异步来弥补多线程缺点
es6常用技术问题?
块级作用域 关键字let, 常量const
赋值解构
let singer = { first: "Bob", last: "Dylan" };
let { first: f, last: l } = singer; // f = "Bob", l = "Dylan"
let [a, b, c] = [1,2, 3] // a =1 ,b =2 , c =3
let [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25");
let [x, y] = [1, 2, 3]; // x = 1, y = 2
let singer = { first: "Bob", last: "Dylan" };
let { first: f, last: l } = singer; // f = "Bob", l = "Dylan"
let [a, b, c] = [1,2, 3] // a =1 ,b =2 , c =3
let [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25");
let [x, y] = [1, 2, 3]; // x = 1, y = 2
函数参数 - 默认值、参数打包、 数组展开(Default 、Rest 、Spread)
//es6
function findArtist(name='lu', age='26') {
...
}
//es5
function findArtist(name, age) {
var _name = name || 'lu';
var _age = age || '26';
...
}
//Rest
function f(x, ...y) {
// y is an Array
console.log(y);
return x * y.length;
}
// ['hello', true]
f(3, "hello", true) == 6
//Spread
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
箭头函数 Arrow functions
简化了代码形式,默认return表达式结果。
cont b = a => a + '(demo)';
cont b = function(a) {
return a + '(demo)'
}
字符串模板 Template strings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// return "Hello Bob, how are you today?"
Iterators(迭代器)+ for…of
for (var n of ['a','b','c']) {
console.log(n);
}
// 打印a、b、c
for (const [index, info] of ['a','b','c'].entries()} {
}
for (const [key, val] of Object.entries({a:1, b:2})} {
}
Class
Class,有constructor、extends、super,但本质上是语法糖(对语言的功能并没有影响,但是更方便程序员使用)。
//es6
class Artist {
constructor(name) {
this.name = name;
}
perform() {
return this.name + " performs ";
}
}
class Singer extends Artist {
constructor(name, song) {
super.constructor(name);
this.song = song;
}
perform() {
return super.perform() + "[" + this.song + "]";
}
}
//es5
function Artist(name) {
this.name = name;
this.perform = function() {
return this.name + " performs ";
}
}
Artist.prototype.perform = function () {
return this.name + " performs ";
};
function Singer(song, name) {
this.song = song;
Artist.call(this, name);
this.perform = function() {
return this.perform() + "[" + this.song + "]";
}
}
// es6
let james = new Singer("Etta James", "At last");
james instanceof Artist; // true
james instanceof Singer; // true
james.perform(); // "Etta James performs [At last]"
//es5
Singer.prototype = Object.create(Artist.prototype);
Singer.prototype.constructor = Artist;
var james = new Singer("Etta James", "At last");
Modules
ES6的内置模块功能借鉴了CommonJS和AMD各自的优点:
(1).具有CommonJS的精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点。
(2).类似AMD,支持异步加载和可配置的模块加载。
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
Module Loaders:
// Dynamic loading – ‘System’ is default loader
System.import('lib/math').then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Directly manipulate module cache
System.get('jquery');
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
//es5
document.write("<script language=javascript src='*.js'></script>");
Map + Set + WeakMap + WeakSet
四种集合类型,WeakMap、WeakSet作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
//WeakMap
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });//Because the added object has no other references, it will not be held in the set
Math + Number + String + Array + Object APIs
一些新的API
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })
Proxies
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情。对象属性拦截修改。
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
可监听的操作: get、set、has、deleteProperty、apply、construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible。
Symbols
Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。
var key = Symbol("key");
var key2 = Symbol("key");
key == key2 //false
Promises
Promises是处理异步操作的对象,使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观(类似jQuery的deferred 对象)。
这里我建议使用es6 新更新 的 async 异步操作,更简洁方便
function fakeAjax(url) {
return new Promise(function (resolve, reject) {
// setTimeouts are for effect, typically we would handle XHR
if (!url) {
return setTimeout(reject, 1000);
}
return setTimeout(resolve, 1000);
});
}
// no url, promise rejected
fakeAjax().then(function () {
console.log('success');
},function () {
console.log('fail');
});
// Ajax 是异步的,异步函数调用异步函数
async a () {
const response = await Ajax();
return response.id;
}
async c() {
const result = await this.a();
}
// 调用异步函数
b () {
a().then(result => {
console.log(result);
})
}