1.const let var 区别?
- var声明变量存在变量提升,let和const不存在变量提升
- let声明变量和const声明常量,两个都可以形成块级作用域
-
const 声明之后必须马上赋值,否则会报错
-
const 简单类型一旦声明就不能再更改,复杂类型(数组、对象等)指针指向的地址不能更改,内部数据可以更改。
- var是不受限于块级的,而let是受限于块级
- ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明
- const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
- var会与window相映射(会挂一个属性),而let不与window相映射
2.变量提升:变量提升会优先声明
变量提升意思是 var声明变量时 先声明 再赋值
var a = 5;
function test3(){
var a;
cosnole.log(a)
}
test3(); ------------>undefined
var a = 5;
function test3(){
var a = 3;
console.log(a)
}
test3() -------------->3 (变量提升)
var a = 1;
function foo() {
var a = function(){} ----->函数提升
a = 10;
console.log(a)------------10
return
}
foo()
console.log(a)--------------1
匿名函数不会被提升
3.什么是匿名函数
- 语法:(function(){...}());
- 前面的括号包含函数体,后面的括号就是给匿名函数传递参数并立即执行之
- 匿名函数的作用是避免全局变量的污染以及函数名的冲突
- 小括号的作用:小括号对返回的,就是一个匿名函数的Function 对象
(function(x,y){
alert(x+y);
return x+y;
}(3,4));
4.JS执行上下文
当代码运行时,会产生一个对应的执行环境,在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 undefined,代码从上往下开始执行,就叫做执行上下文。 (代码从上往下执行的过程就是执行上下文)
运行环境:
5.js中this对象的指代
6.如何判断一个对象是否属于某个类
if(spear instanceof Array){console.log('yes');}
优点:
- 语法简洁,函数定义不再使用关键字function(),而是用()=>来进行定义
- 解决了this指向的问题,有一个确定的作用域
- 如果只有一个形参,可以不打小括号
- 如果只有一条执行语句,可以不打大括号
- 只有一条执行语句,并且需要返回这条语句的结果,可以不用写return
注意:
- 箭头函数没有自己的this,它的this是继承而来,默认指向在定义它时所处的对象(宿主对象)
- 不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
- 不能够使用arguments对象
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数
关于箭头函数中的this的指向_xu4321083的博客-CSDN博客_箭头函数中的this
举例子:
箭头函数的写法:
// ES5
var x = function(x, y) {
return x * y;
}
// ES6
const x = (x, y) => x * y;
- 箭头函数是匿名函数的一种简写方式
- ()内是参数,一个参数的时候可以省略
- =>后面是返回值,省略了return
- 词法作用域,不会绑定this;箭头函数会默认绑定外层的this的值,所以箭头函数中this的值和外层this的值是一样的
- 如果函数部分只是一个语句,则可以省略return关键字和大括号{}
以下两者等价
const x = (x, y) => x * y;
const x = (x, y) => { return x * y };
8.模板字符串
模版字符串,用`(反引号)标识,用${}将变量括起来。
var name="zzw";
`${name},no matter what you do,I trust you.`
9. ... 展开运算符
可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
数组合并的时候用
// 数组合并
var array1 = [1, 2, 3,4];
var array2 = [4, 5, 6];
var array3 = [...array1, ...array2, 7, 8]; //[1,2,3,4,4,5,6,7,8]
array1.push(...array2 )// 当然也可以使用concat等数组合并方法,但是扩展运算符提供了一种新的方式
向数组中添加元素
var items = [1,2,3];
var newArr = [...items,4]
console.log(newArr); // [1,2,3,4]
合并对象属性,和Object.assign作用是一样的;
var obj = {name:"zs",age:12};
var obj2 = {age:14,addr:"gz"}
console.log({...obj,...obj2}); // {name:"zs",age:14,addr:"gz"}
10.解构赋值
所谓的解构赋值其实就是分解出一个对象的解构,分成两个步骤:
- 变量的声明
- 变量的赋值
let node = {
type: "Identifier",
name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
数组解构
基本:let [a, b, c] = [1, 2, 3];// a = 1 // b = 2 // c = 3
可嵌套:let [a, [[b], c]] = [1, [[2], 3]]; // a = 1 // b = 2 // c = 3
可忽略:let [a, , b] = [1, 2, 3]; // a = 1 // b = 3
不完全解构:let [a = 1, b] = []; // a = 1, b = undefined
剩余运算符:let [a, ...b] = [1, 2, 3]; //a = 1 //b = [2, 3]
字符串等:let [a, b, c, d, e] = 'hello'; // a = 'h' // b = 'e' // c = 'l' // d = 'l' // e = 'o'
对象模型解构
基本:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'
剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
解构默认值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
11.promise(异步)--解决异步问题
promise是一个对象,可以获取异步操作的消息,promise提供统一的API,各种异步操作都能处理
2个特点:
- 对象的状态不受外界影响 (3种状态)
- Pending状态(进行中)
- Fulfilled状态(已成功)
- Rejected状态(已失败)
- 一旦状态改变就不会再变 (两种状态改变:成功或失败)
- Pending -> Fulfilled
- Pending -> Rejected
var promise = new Promise(function(resolve, reject){
// ... some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
})
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve作用是将Promise对象状态由“未完成”变为“成功”,也就是Pending -> Fulfilled
,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;而reject函数则是将Promise对象状态由“未完成”变为“失败”,也就是Pending -> Rejected
,在异步操作失败时调用,并将异步操作的结果作为参数传递出去。
then:
Promise实例生成后,可用then
方法分别指定两种状态回调参数。then 方法可以接受两个回调函数作为参数:
routeInfo (type = 'edit') {
return new Promise((resolve, reject) => {
this.listData(type).then(result => {
return new Promise((resolve) => {
let res = result.list[0]
if (result.list.length !== 1) {
console.warn('待办结果不唯一')
}
resolve(res)
})
}).then(res => {
this.dataValidator(res, type).then(res => {
resolve({
name: this.info[this.typeCode][type].routerName,
query: this.info[this.typeCode][type].query(res),
params: this.info[this.typeCode][type].params && this.info[this.typeCode][type].params(res)
})
}).catch(e => {
reject(e)
})
}).catch(e => {
reject(e)
})
})
}
all:多个请求一起发出并根据请求顺序获取和使用数据的场景
getData () {
Promise.all([
this.getCreditData(),
this.getProjectData()
]).then(result => {
}).finally(() => {
this.loading.detail = false
})
},
catch:reject状态时才会进入,捕获错误信息
let p2 = new Promise((resolve, reject) => {
reject('123');
}).catch(e => e);
finally() :不管Promise对象成功失败都进finally,都会执行的操作
Promise.then(result => {}).catch(error => {}).finally(() =>{})
11.Symbol(第七种基本数据类型)
Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的
好处:用来定义对象的唯一属性名(类似于id一样是唯一标识)
缺点:Symbol类型是不能通过Object.keys()或者for...in来枚举,因为不包含在对象自身的属性名集合(property name)中
因此:使用JSON.stringify()将对象转成JSON字符串的时候,Symbol这个属性也会排除在外
(1)为什么引入Symbol?
(2)typeof运算符用于Symbol类型值,返回symbol
let name = Symbol()
console.log(typeof name) // Symbol
(3)Symbol的值和谁都不相等,是独一无二的值.
let symbol1 = Symbol()
let symbol2 = Symbol()
console.log(symbol1 === symbol2) // false
console.log(symbol1 == symbol2) // false
symbol1和symbol2相等吗?
不相等.因为Symbol类型的值是独一无二的值.
(4).为什么Symbol不能用new关键字?
let obj = new Symbol() // TypeError
(5)console.log(Symbol(‘Alice’) + “bruce”) // TypeError,为什么错误?
Symbol值不能与其他类型的值进行运算
(6)如何读取Symbol的属性值?
let name = Symbol()
let obj = {}
obj[name] = “Alice”
cosnole.log(obj[name])
Symbol值定义属性时,必须放在方括号内,不能用点运算符。
(7)Symbol的值显示的转成字符串或者布尔值,但是不能转成数值.
Object.getownPropertySymbols() 和 Object.getownPropertyNames()
12.解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
类模板字符串的功能
let name = 'web';
let age = 10;
let str = '你好,${name} 已经 ${age}岁了'
str = str.replace(/\$\{([^}]*)\}/g,function(){
return eval(arguments[1]);
})
console.log(str);//你好,web 已经 10岁了
================开始不用看
了解axios和fetch
9.import、export导入导出
ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用
什么是Proxy? (代理--不会)
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
93.requireJS的核心原理是什么?
require.js 的核心原理是通过动态创建 script 脚本来异步引入模块,然后对每个脚本的 load 事件进行监听,如果每个脚本都加载完成了,再调用回调函数。
三种事件模型是什么?
事件 是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型。
-
DOM0级模型: ,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js属性来指定监听函数。这种方式是所有浏览器都兼容的。
-
IE 事件模型: 在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
-
DOM2 级事件模型: 在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。
92.JS中的宿主对象与原生对象有何不同?( 请指出 JavaScript 宿主对象 (host objects) 和原生对象 (native objects) 的区别?)
宿主对象:这些是运行环境提供的对象。这意味着它们在不同的环境下是不同的。例如,浏览器包含像windows
这样的对象,但是Node.js环境提供像Node List
这样的对象。 Javascript内置对象、原生对象、宿主对象关系_每天=生命的最后一天-CSDN博客
原生对象:这些是JS中的内置对象。它们也被称为全局对象,因为如果使用JS,内置对象不受是运行环境影响。
- p.proto
- p.constructor.prototype
- Object.getPrototypeOf(p)
从对象实例获得父级原型对象:1.对象实例._proto_ 2.Object.getPrototypeOf(对象实例)
function Student(){
this.name = "小马扎";
this.age = 18;
}
var lilei = new Student(); // 创建对象实例
console.log(Student.prototype); //Student{}
console.log(lilei.__proto__); //Student{}
console.log(Object.getPrototypeOf(lilei)); //Student{}
JS中为什么需要原型对象
JS使用构造函数创建对象时,每个实例对象的方法都存放在独立的内存中,不能共享;使用原型对象可以解决上面浪费内存空间。
https://jingyan.baidu.com/article/597a0643a743aa712b52439b.html
JavaScript如果实现继承?
1、构造函数继承
2、原型链继承
3、实例继承
4、拷贝继承
原型prototype机制或apply和call方法去实现较简单、建议使用构造函数与原型混合方式
function Parent(){
this.name = "shen";
}
function Child(){
this.age = 23;
}
Child.prototype = new Parent();
var Demo = new Child();
console.log(Demo.age);
console.log(Demo.name);
JavaScript原型、原型链?有什么特点?
什么是原型:原型是一个对象、其他对象可以通过它实现属性继承(任何对象都可以成为原型)
所有对象在默认情况下都有一个原型(原型本身就是个对象)
什么是对象(任何无序键值对的集合)如果它不是基本数据类型、那它就是一个对象
原型链:当我们访问这个对象的属性时、如果这个对象内部不存在这个属性、那么他就回去prototype里去查找、这个prototype又有自己的prototype属性。这样一直找下去。就是平时说的原型链概念
关系:instance.constructor.prototype = instance.__proto__
当我们需要一个属性时、JavaScript引擎会先查看当前对象中是否有这个属性
如果没有、就会查找他的prototype对象是否有这个属性、依次递推下去、一直检索到Object内检对象
function Func(){}
Func.prototype.name = "shen";
Func.prototype.getInfo = function(){
return this.name;
}
var person = new Func();
console.log(person.getInfo());
console.log(Func.prototype);
var a = {};
a.__proto__ = Array.prototype;
a.length;
创建a、然后通过a的原型来达到继承Array这个已经存在的对象的功能。
原型真正的美丽提现在多个实例、共用一个通用原型
原型对象的属性一旦定义、就可以被多个引用它的实例所继承、性能和未还的意义是很强的(这也是构造函数存在的意义)
每个函数都有一个原型属性。JavaScript没有在构造函数(constructor)和其他函数之间做区分。
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/10
ES5/ES6 的继承除了写法以外还有什么区别?
先看ES5的继承(原型链继承)
function a() {
this.name = 'a';
}
a.prototype.getName = function getName() {
return this.name
}
function b() {}
b.prototype = new a();
console.log(b.prototype.__proto__ === a.prototype); // true
console.log(b.__proto__ === a); // false
console.log(b.__proto__); // [Function]
ES6继承
class A {
constructor(a) {
this.name = a;
}
getName() {
return this.name;
}
}
class B extends A{
constructor() {
super();
}
}
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(B.__proto__ === A); // true
console.log(B.__proto__); // [Function: A]
对比代码可以知道,子类的继承都是成功的,但是问题出在,子类的 __proto__
指向不一样。
ES5
的子类和父类一样,都是先创建好,再实现继承的,所以它们的指向都是 [Function]
。
ES6
则得到不一样的结果,它指向父类,那么我们应该能推算出来,它的子类是通过 super
来改造的。
根据 es6--阮一峰 在class继承里面的说法,是这样子的:
引用阮一峰的 ECMAScript6入门
的class继承篇
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。
ES5 的继承,实质是先创造子类的实例对象this
,然后再将父类的方法添加到this
上面(Parent.apply(this)
)。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this
上面(所以必须先调用super
方法),然后再用子类的构造函数修改this
。
1、class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
2、class 声明内部会启用严格模式。
3、class 的所有方法(包括静态方法和实例方法)都是不可枚举的。
4、class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
5、必须使用 new 调用 class。
6、class 内部无法重写类名
setTimeout、Promise、Async/Await 的区别
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行; promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
谈谈以前端角度出发做好SEO需要考虑什么?
Meta标签优化
主要包括主题(Title),网站描述(Description),和关键词(Keywords)。还有一些其它的隐藏文字比如Author(作者),Category(目录),Language(编码语种)等。
合理的标签使用
hasOwnProperty:当前函数返回的是一个布尔值、指出一个对象是否具有指定名称的属性、此方法无法检查该对象的原型链中是否具有该属性(该属性必须是对象本身的一个成员属性)
object.hasOwnProperty(prName);
参数object是必选项、一个对象是实例
参数proName是必选项。一个属性名称的字符串值。
如果object具有制定名称的属性、那么JavaScript中的hasOwnProperty函数方法返回true、反之则返回false
JavaScript设计模式
- 单例模式
- 适配器模式
- 代理模式
- 发布-订阅模式
- 策略模式
- 迭代器模式
52.ajax原理:
- 创建对象 var xhr = new XMLHTTPRequest()
- 打开请求 xhr.open('GET','example.txt',true)
- 发送请求 xhr.send()
- 接收响应数据 xhr.onready.stateChange = function()
考点:日期对象Date相关API的使用 var d = new Date();
65.foo = foo||bar ,这行代码是什么意思?为什么要这样写?
var foo;
if(foo){
foo=foo;
}else{
foo=bar;
}
答案:常用于函数参数的空判断
短路表达式:作为”&&”和”||”操作符的操作数表达式,这些表达式在进行求值时,只要最终的结果已经可以确定是真或假,求值过程便告终止,这称之为短路求值。
考点:if条件的真假判定
记住以下是false的情况:空字符串、false、undefined、null、0
(头条、微医)Async/Await 如何通过同步的方式实现异步
async await 用于把异步请求变为同步请求的方式,第一个请求的返回值作为后面一个请求的参数,其中每一个参数都是一个promise对象
例如:这种情况工作中会经常遇到
(async () => {
var a = await A();
var b = await B(a);
var c = await C(b);
var d = await D(c);
})();
(头条)异步笔试题
请写出下面代码的运行结果
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
(携程)算法手写题
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
// 方法一
console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))
// 方法二
console.log(Array.from(new Set(arr.toString().split(','))).map(Number).sort((a, b) => a - b))
// 方法三
// 第一步:扁平化
let newArr = [];
function flat(originArr) {
if ({}.toString.call(originArr) === '[object Array]') {
for (let i of originArr) {
if ({}.toString.call(i) === '[object Array]') {
arguments.callee(i)
} else {
newArr.push(i)
}
}
}
return newArr;
}
console.log(flat(arr))
// 第二步:去重
var newArr1 = [];
for (let i of newArr) {
if (!newArr1.includes(i)) newArr1.push(i);
}
// 第三步:排序 可以采用相关算法处理
console.log(newArr1.sort((a, b) => a - b))
(兑吧)情人节福利题,如何实现一个 new
先理清楚 new 关键字调用函数都的具体过程,那么写出来就很清楚了
首先创建一个空的对象,空对象的 ___proto____属性指向构造函数的原型对象
把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象
如果构造函数返回一个非基本类型的值,则返回这个值,否则上面创建的对象
function _new(fn, ...arg) {
var obj = Object.create(fn.prototype);
const result = fn.apply(obj, ...arg);
return Object.prototype.toString.call(result) == '[object Object]' ? result : obj;
}
(网易)简单讲解一下http2的多路复用
第 15 题:简单讲解一下 http2 的多路复用 · Issue #14 · Advanced-Frontend/Daily-Interview-Question · GitHub
谈谈你对TCP三次握手和四次挥手的理解
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/15
A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/21
介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/22
找出多维数组最大值
sort背后原理是什么?
JavaScript 中 this 是如何工作的
JavaScript 中 this 是如何工作的 ? - 前端の战士 - 博客园
一般this指向调用者,箭头函数的this,指向调用的父作用域
call ,apply,bind修改this
页面重构怎么操作?
new实现
js new一个对象的过程,实现一个简单的new方法 - 听风是风 - 博客园
列出JS中的一些设计模式:
- 创建模式:该模式抽象了对象实例化过程。
- 结构型模式:这些模式处理不同的类和对象以提供新功能。
- 行为模式:也称发布-订阅模式,定义了一个被观察者和多个观察者的、一对多的对象关系。
- 并行设计模式:这些模式处理多线程编程范例。
- 架构设计模式:这些模式用于处理架构设计。
只需给现有函数赋值,就可以很容易地在现有函数中添加新属性。例如,现有一个对象person
,通过下面的代码来为 person
添加新的属性:
person.country= “India”;
JavaScript 原型,原型链?有什么特点?
每个对象都会在其内部初始化一个属性,就是prototype(原型)==(在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。)
function Person(age) {
this.age = age
}
Person.prototype.name = 'kavin'
var person1 = new Person()
var person2 = new Person()
console.log(person1.name) //kavin
console.log(person2.name) //kavin
原型链:当我们去访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里面找这个属性,这个属性prototype就会有自己的prototype。
就这样一层一层的寻找,也就是我们平时所说的原型链的概念。
关系
instance.constructor.prototype=instance._proto_
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与
之相关的对象也会继承这一改变。
谈谈你对模块化开发的理解?
- 我对模块的理解是,一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
- 由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污染,并且模块间没有联系。
- 后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所有的所有的模块成员,外部代码可以修改内部属性的值。
- 现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。
js 的几种模块规范?
js 中现在比较成熟的有四种模块加载方案:
-
第一种是 Commonjs 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
-
第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。
-
第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
-
第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。这种方案和上面三种方案都不同。
61.JavaScript事件循环机制相关问题
关键字:单线程非阻塞、执行栈、事件队列、宏任务(setTimeout()、setInterval(),setTimeout、setInterval、script、setImmedate、IO、Promise.resolve)、微任务(new Promise(),process.nextTick、Promise、async await、mutation observer)
- 宏任务、微任务、同步任务的执行顺序
setTimeout(function () { console.log(1); }); new Promise(function(resolve,reject){ console.log(2) resolve(3) }).then(function(val){ console.log(val); }) console.log(4); // 2 // 4 // 3 // 1
先按顺序执行同步任务,Promise新建后立即执行输出2,接着输出4,异步任务等同步任务执行完后执行,且同一次事件循环中,微任务永远在宏任务之前执行。这时候执行栈空了,执行事件队列,先取出微任务,输出3,最后取出一个宏任务,输出1。
事件循环--浏览器
执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。
对象是什么?
arguments对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它有一个length属性,我们可以使用数组索引表示法arguments[1]来访问单个值,但它没有数组中的内置方法,如:forEach、reduce、filter和map。
我们可以使用Array.prototype.slice将arguments对象转换成一个数组。
function one() {
return Array.prototype.slice.call(arguments);
}
注意:箭头函数中没有arguments对象。
function one() {
return arguments;
}
const two = function () {
return arguments;
}
const three = function three() {
return arguments;
}
const four = () => arguments;
four(); // Throws an error - arguments is not defined
当我们调用函数four时,它会抛出一个ReferenceError: arguments is not defined error
。使用rest语法,可以解决这个问题
const four = (...args) => args;
这会自动将所有参数值放入数组中。
ES6 模块与 Commonjs 模块、AMD、CMD 的差异。
https://blog.csdn.net/u013681954/article/details/97299399
generator
调用之后会返回一个value、next 和done,value表示当前的值,done表示是否结束,结束后值为 true,每次执行都需要调用next才会继续执行
9.修饰器 @
decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数
10.class 类的继承
ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念
23.Promise 中reject 和 catch 处理上有什么 区别
reject 是用来抛出异常,catch 是用来处理异常
reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch
网络异常(比如断网),会直接进入catch而不会进入then的第二个回调
14.Proxy代理
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情
15.ECMAScript 6 怎么写 class ,为何会出现 class?
ES6的class可以看作是一个语法糖,它的绝大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法
//定义类
class Point {
constructor(x,y) {
//构造方法
this.x = x; //this关键字代表实例对象
this.y = y;
} toString() {
return '(' + this.x + ',' + this.y + ')';
}
}
19.下面的输出结果是多少
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
promise.then(() => {
console.log(3);
})
console.log(4); //1 2 4 3
Promise 新建后立即执行,所以会先输出 1,2,而 Promise.then()
内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后输出3
20.使用结构赋值,实现两个变量的值的交换
let a = 1;let b = 2;
[a,b] = [b,a];
let name = Symbol('name');
let product = {
[name]:"洗衣机",
"price":799
};
Reflect.ownKeys(product);
22.下面Set结构,打印出的size值是多少
let s = new Set();
s.add([1]);
s.add([1]);console.log(s.size); //2
两个数组[1]并不是同一个值,它们分别定义的数组,在内存中分别对应着不同的存储地址,因此并不是相同的值。都能存储到Set结构中,所以size为2
24.使用class 手写一个promise
//创建一个Promise的类
class Promise{
constructor(executer){//构造函数constructor里面是个执行器
this.status = 'pending';//默认的状态 pending
this.value = undefined//成功的值默认undefined
this.reason = undefined//失败的值默认undefined
//状态只有在pending时候才能改变
let resolveFn = value =>{
//判断只有等待时才能resolve成功
if(this.status == pending){
this.status = 'resolve';
this.value = value;
}
}
//判断只有等待时才能reject失败
let rejectFn = reason =>{
if(this.status == pending){
this.status = 'reject';
this.reason = reason;
}
}
try{
//把resolve和reject两个函数传给执行器executer
executer(resolve,reject);
}catch(e){
reject(e);//失败的话进catch
}
}
then(onFufilled,onReject){
//如果状态成功调用onFufilled
if(this.status = 'resolve'){
onFufilled(this.value);
}
//如果状态失败调用onReject
if(this.status = 'reject'){
onReject(this.reason);
}
}
}
29.说一下es6的导入导出模块
// 只导入一个 import {sum} from "./example.js" // 导入多个 import {sum,multiply,time} from "./exportExample.js" // 导入一整个模块 import * as example from "./exportExample.js"
导出通过export关键字
//可以将export放在任何变量,函数或类声明的前面
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//也可以使用大括号指定所要输出的一组变量
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
//使用export default时,对应的import语句不需要使用大括号
let bosh = function crs(){}
export default bosh;
import crc from 'crc';
//不使用export default时,对应的import语句需要使用大括号
let bosh = function crs(){}
export bosh;
import {crc} from 'crc';
13.
堆栈内存(没看)
当浏览器加载页面时,会形成两个虚拟的内存;一个栈内存,堆内存;
栈内存:
- 提供代码的运行环境;
- 存储基本数据类型;
堆内存: 存储引用数据类型
;
28.函数的递归
递归: 针对的是函数; 是JS中一种重要的思想;
函数: 分为定义和执行
函数递归: 在函数体内部,调用函数自己本身,让其执行;这就是递归;
function sum(num) {
if(num === 0){
return num;
}
if(num%3===0){
return num + sum(num-1);
}
if(num%3!==0){
return sum(num-1);
}
// return sum(99)
// return 99 + sum(98)
// return 99 + sum(97)
// return 99 + sum(96)
// return 99 + 96 + sum(95)....
// return 99 + 96 +... + 3 + sum(2)
// return 99 + 96 +... + 3 + sum(1)
// return 99 + 96 +... + 3 + 0
}
console.log(sum(100));
console.log(sum(200));
29.求1-100之间是3倍数的和(% : 取模;取余数)
var total = 0;
for(var i=0;i<100;i++){
if(i%3===0){
total += i;
}
}
30.数组的插入排序
var ary = [12,88,67,98,56,35,45,26,1];
//5 6 7 8 10
//得到的新数组是排好序
//插入排序原理:去原有数组中去取每一项,然后和新数组中的成员进行比较,从新数组后面开始,一一向前比较,比新数组项小,继续和前一项比较,直到当前项比数组中一项值大时,把它放到这一项的后面;
var newAry = [];
newAry.push(ary[0]);
for(var i=1;i<ary.length;i++){
var cur = ary[i];
for(var j=newAry.length-1;j>=0;j--){
if(cur<newAry[j]){// 如果数组中拿到的这一项比新数组中j的这一项小;那么继续j--;和上一个成员进行比较;
if(j===0){
newAry.unshift(cur);
break;// 只会中止当前的小循环
}
}else{
// cur<newAry[j]的情况,把当前项放到新数组这一项的后面;
newAry.splice(j+1,0,cur);
break;
}
}
}
// 从前向后
console.log(newAry);
var ary = [12,88,67,98,56,35,45,26,1];
var mAry = [12,67,88];
mAry.push(ary[0]);
for(var i=1;i<ary.length;i++){
var cur = ary[i];
for(var j = mAry.length-1;j>=0;j--){
if(cur<mAry[j]){
if(j===0){
mAry.unshift(cur);
break;
}
}else{
mAry.splice(j+1,0,cur);
break;
}
}
}
32.随机验证码
var code = document.getElementById("code");
function getCode() {
// 准备一个62个字符串;
// 产生随机数;随机数可以作为字符的索引;
// 随机索引范围【0-61】
var str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMnopQRSTUVWXYZ";
// 随机数;
var i =0;
var newStr = "";
while(i<4){
var num = Math.round(Math.random()*(61-0)+0);
newStr+=str[num];
i++;
}
code.innerHTML = newStr;
}
getCode();
// 把函数的空间地址赋值给code的onclick属性;
code.onclick = getCode;
var foo = "11"+2-"1";
console.log(foo);//112-1=111
console.log(typeof foo);//”number”
考点:
1、数字和字符串都可以用加法运算符,数字和字符串相加,结果就是一个字符串
2、但是减法运算符只能用于两个数字之间,想要执行减法运算,必须把两边数字都变成数字类型的
答案:”111”、”number”
45.已知数组var stringArray = [“This”, “is”, “Baidu”, “Campus”],alert出”This is Baidu Campus”。
考点:数组的join方法的使用,将一个数组通过指定字符分隔组成一个想要的字符串
答案:alert(stringArray.join(“ ”))
46.已知有字符串foo=”get-element-by-id”,写一个function将其转化成驼峰表示法”getElementById”。
function combo(msg){
var arr=msg.split("-");//[get,element,by,id]
for(var i=1;i<arr.length;i++){
arr[i]=arr[i].[0].toupperCase()+arr[i].substring(1);//Element
}
msg=arr.join("");//msg=” getElementById”
return msg;
}
47.var numberArray = [3,6,2,4,1,5];
1)实现对该数组的倒排,输出[5,1,4,2,6,3]
function reverseArray(arr){
var result=[];
//方法1:
/*for (var i = arr.length - 1; i >= 0; i--) {
result.push(arr[i]);
}*/
//方法2:
for (var i = 0, len = arr.length; i < len; i++) {
result.unshift(arr[i]);
}
return result;
}
2)实现对该数组的降序排列,输出[6,5,4,3,2,1]
function sortDesc(arr) {
for (var i = 0, len = arr.length; i < len; i++) {
for (var j = i + 1, len2 = arr.length; j < len2; j++) {
//>就是降序 <就是升序
if (arr[j] > arr[i]) {
var temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
var iArray = [];
function getRandom(istart, iend){
var iChoice = iend - istart +1;
return Math.floor(Math.random() * iChoice+ istart);
}
Math.random()就是获取0-1之间的随机数(永远获取不到1)
for(var i=0; i<10; i++){
var result= getRandom(10,100);
iArray.push(result);
}
iArray.sort();
52.把两个数组合并,并删除第二个元素。
考点:1、数组的concat、splice用法
splice() 方法删除数组的元素,或者向数组中添加元素,然后返回被删除的项目。
参数1:从何处开始删除数组的元素(使用负数则从数组的末尾开始)
参数2:要删除的元素个数(如果是0,就代表不删除)
参数3,4,5。。。:要添加的元素
var array1 = ['a','b','c'];
var bArray = [‘d’,’e’,’f’];
var cArray = array1.concat(bArray);
cArray.splice(1,1);
54.Javascript中, 以下哪条语句一定会产生运行错误? 答案( BC ) ??????????????
A、var _变量=NaN; //
B、var 0bj = []; // Invalid or unexpected token
C、var obj = //;
D、var obj = {};
//正确答案:BC
56.Javascript创建对象的几种方式?
构造函数方式,原型模式,混合构造函数原型模式,工厂方式,动态原型方式
混合构造函数+原型模式:
function Robot(name){
this.name=name;
}
Robot.prototype.say=function(){
alert("大家好,我叫"+this.name);
};
var alphaGo=new Robot("阿尔法狗");
alphaGo.say();
工厂方式:
function create(name,age){
var o={};
o.name=name;
o.age=age;
return o;
}
var p1=create("张三",20);
动态原型方式:
function Person(name,work){
this.name=name;
if(work){
Person.prototype.doWork=function(){
alert("我正在从事"+work+"的工作");
}
}
}
var p1=new Person("姚明");
var p2=new Person("喵喵","程序猿鼓励师");
59.documen.write和 innerHTML 的区别?
61.解释jsonp的原理,以及为什么不是真正的ajax
动态创建script标签,回调函数
Ajax是页面无刷新请求数据操作
62.将数字 12345678 转化成 RMB形式 如: 12,345,678
//思路:先将数字转为字符, str= str + ” ;
//利用反转函数,每三位字符加一个 ‘,’最后一位不加; re()是自定义的反转函数,最后再反转回去!
for(var i = 1; i <= re(str).length; i++){
tmp += re(str)[i - 1];
if(i % 3 == 0 && i != re(str).length){
tmp += ',';
}
}
64.什么是三元表达式 (Ternary expression)?“三元 (Ternary)” 表示什么意思?
一个运算符如果有一个操作数,为一元运算符,两个为二元,三个为三元运算符,三元表达式则为一个三元运算表达式!
93.js有几种弹出窗
alert.confirm,prompt
- alert:警告窗
- confirm:确认窗口
- prompt:信息输入窗口
find和filter的区别
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。