最后
基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
所有这些console.log
语句都返回true
,即使我们传递的值不是NaN
。
在ES6
中,建议使用Number.isNaN
方法,因为它确实会检查该值(如果确实是NaN
),或者我们可以使自己的辅助函数检查此问题,因为在 JS 中,NaN是唯一的值,它不等于自己。
function checkIfNaN(value) {
return value !== value;
}
57. 如何判断值是否为数组?
我们可以使用Array.isArray
方法来检查值是否为数组。 当传递给它的参数是数组时,它返回true
,否则返回false
。
console.log(Array.isArray(5)); // false
console.log(Array.isArray(“”)); // false
console.log(Array.isArray()); // false
console.log(Array.isArray(null)); // false
console.log(Array.isArray({ length: 5 })); // false
console.log(Array.isArray([])); // true
如果环境不支持此方法,则可以使用polyfill
实现。
function isArray(value){
return Object.prototype.toString.call(value) === “[object Array]”
}
当然还可以使用传统的方法:
let a = []
if (a instanceof Array) {
console.log(‘是数组’)
} else {
console.log(‘非数组’)
}
58. 如何在不使用%
模运算符的情况下检查一个数字是否是偶数?
我们可以对这个问题使用按位&
运算符,&
对其操作数进行运算,并将其视为二进制值,然后执行与运算。
function isEven(num) {
if (num & 1) {
return false
} else {
return true
}
}
0
二进制数是 000
1
二进制数是 001
2
二进制数是 010
3
二进制数是 011
4
二进制数是 100
5
二进制数是 101
6
二进制数是 110
7
二进制数是 111
以此类推…
与运算的规则如下:
| a | b | a & b |
| :-- | :-- | :-- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 1 | 1 |
因此,当我们执行console.log(5&1)
这个表达式时,结果为1
。首先,&
运算符将两个数字都转换为二进制,因此5
变为101
,1
变为001
。
然后,它使用按位怀运算符比较每个位(0
和1
)。 101&001
,从表中可以看出,如果a & b
为1
,所以5&1
结果为1
。
| 101 & 001 |
| — |
| 101 |
| 001 |
| 001 |
-
首先我们比较最左边的
1&0
,结果是0
。 -
然后我们比较中间的
0&0
,结果是0
。 -
然后我们比较最后
1&1
,结果是1
。 -
最后,得到一个二进制数
001
,对应的十进制数,即1
。
由此我们也可以算出console.log(4 & 1)
结果为0
。知道4
的最后一位是0
,而0 & 1
将是0
。如果你很难理解这一点,我们可以使用递归函数来解决此问题。
function isEven(num) {
if (num < 0 || num === 1) return false;
if (num == 0) return true;
return isEven(num - 2);
}
59. 如何检查对象中是否存在某个属性?
检查对象中是否存在属性有三种方法。
第一种使用 in
操作符号:
const o = {
“prop” : “bwahahah”,
“prop2” : “hweasa”
};
console.log(“prop” in o); // true
console.log(“prop1” in o); // false
第二种使用 hasOwnProperty
方法,hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
console.log(o.hasOwnProperty(“prop2”)); // true
console.log(o.hasOwnProperty(“prop1”)); // false
第三种使用括号符号obj["prop"]
。如果属性存在,它将返回该属性的值,否则将返回undefined
。
console.log(o[“prop”]); // “bwahahah”
console.log(o[“prop1”]); // undefined
60. AJAX 是什么?
即异步的 JavaScript 和 XML,是一种用于创建快速动态网页的技术,传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。使用AJAX则不需要加载更新整个网页,实现部分内容更新
用到AJAX的技术:
-
HTML - 网页结构
-
CSS - 网页的样式
-
JavaScript - 操作网页的行为和更新DOM
-
XMLHttpRequest API - 用于从服务器发送和获取数据
-
PHP,Python,Nodejs - 某些服务器端语言
61. 如何在 JS 中创建对象?
使用对象字面量:
const o = {
name: “前端小智”,
greeting() {
return Hi, 我是${this.name}
;
}
};
o.greeting(); // “Hi, 我是前端小智”
使用构造函数:
function Person(name) {
this.name = name;
}
Person.prototype.greeting = function () {
return Hi, 我是${this.name}
;
}
const mark = new Person(“前端小智”);
mark.greeting(); // “Hi, 我是前端小智”
使用 Object.create 方法:
const n = {
greeting() {
return Hi, 我是${this.name}
;
}
};
const o = Object.create(n);
o.name = “前端小智”;
62. Object.seal 和 Object.freeze 方法之间有什么区别?
Object.freeze()
Object.freeze()
方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze()
返回和传入的参数相同的对象。
Object.seal()
Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。
方法的相同点:
-
ES5新增。
-
对象不可能扩展,也就是不能再添加新的属性或者方法。
-
对象已有属性不允许被删除。
-
对象属性特性不可以重新配置。
方法不同点:
Object.seal
方法生成的密封对象,如果属性是可写的,那么可以修改属性值。
* Object.freeze
方法生成的冻结对象,属性都是不可写的,也就是属性值无法更改。
63. in
运算符和 Object.hasOwnProperty
方法有什么区别?
hasOwnPropert方法
hasOwnPropert()
方法返回值是一个布尔值,指示对象自身属性中是否具有指定的属性,因此这个方法会忽略掉那些从原型链上继承到的属性。
看下面的例子:
Object.prototype.phone= ‘15345025546’;
let obj = {
name: ‘前端小智’,
age: ‘28’
}
console.log(obj.hasOwnProperty(‘phone’)) // false
console.log(obj.hasOwnProperty(‘name’)) // true
可以看到,如果在函数原型上定义一个变量phone
,hasOwnProperty
方法会直接忽略掉。
in 运算符
如果指定的属性在指定的对象或其原型链中,则in
运算符返回true
。
还是用上面的例子来演示:
console.log(‘phone’ in obj) // true
可以看到in
运算符会检查它或者其原型链是否包含具有指定名称的属性。
64. 有哪些方法可以处理 JS 中的异步代码?
-
回调
-
Promise
-
async/await
-
还有一些库: async.js, bluebird, q, co
65. 函数表达式和函数声明之间有什么区别?
看下面的例子:
hoistedFunc();
notHoistedFunc();
function hoistedFunc(){
console.log(“注意:我会被提升”);
}
var notHoistedFunc = function(){
console.log(“注意:我没有被提升”);
}
notHoistedFunc
调用抛出异常:Uncaught TypeError: notHoistedFunc is not a function
,而hoistedFunc
调用不会,因为hoistedFunc
会被提升到作用域的顶部,而notHoistedFunc
不会。
66. 调用函数,可以使用哪些方法?
在 JS 中有4种方法可以调用函数。
作为函数调用——如果一个函数没有作为方法、构造函数、apply
、call
调用时,此时 this
指向的是 window
对象(非严格模式)
//Global Scope
function add(a,b){
console.log(this);
return a + b;
}
add(1,5); // 打印 “window” 对象和 6
const o = {
method(callback){
callback();
}
}
o.method(function (){
console.log(this); // 打印 “window” 对象
});
作为方法调用——如果一个对象的属性有一个函数的值,我们就称它为方法。调用该方法时,该方法的this
值指向该对象。
const details = {
name : “Marko”,
getName(){
return this.name;
}
}
details.getName(); // Marko
作为构造函数的调用-如果在函数之前使用new
关键字调用了函数,则该函数称为构造函数
。构造函数里面会默认创建一个空对象,并将this
指向该对象。
function Employee(name, position, yearHired) {
// 创建一个空对象 {}
// 然后将空对象分配给“this”关键字
// this = {};
this.name = name;
this.position = position;
this.yearHired = yearHired;
// 如果没有指定 return ,这里会默认返回 this
};
const emp = new Employee(“Marko Polo”, “Software Developer”, 2017);
使用apply
和call
方法调用——如果我们想显式地指定一个函数的this
值,我们可以使用这些方法,这些方法对所有函数都可用。
const obj1 = {
result:0
};
const obj2 = {
result:0
};
function reduceAdd(){
let result = 0;
for(let i = 0, len = arguments.length; i < len; i++){
result += arguments[i];
}
this.result = result;
}
reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // reduceAdd 函数中的 this 对象将是 obj1
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // reduceAdd 函数中的 this 对象将是 obj2
67. 什么是缓存及它有什么作用?
缓存是建立一个函数的过程,这个函数能够记住之前计算的结果或值。使用缓存函数是为了避免在最后一次使用相同参数的计算中已经执行的函数的计算。这节省了时间,但也有不利的一面,即我们将消耗更多的内存来保存以前的结果。
68. 手动实现缓存方法]
function memoize(fn) {
const cache = {};
return function (param) {
if (cache[param]) {
console.log(‘cached’);
return cache[param];
} else {
let result = fn(param);
cache[param] = result;
console.log(not cached
);
return result;
}
}
}
const toUpper = (str =“”)=> str.toUpperCase();
const toUpperMemoized = memoize(toUpper);
toUpperMemoized(“abcdef”);
toUpperMemoized(“abcdef”);
这个缓存函数适用于接受一个参数。 我们需要改变下,让它接受多个参数。
const slice = Array.prototype.slice;
function memoize(fn) {
const cache = {};
return (…args) => {
const params = slice.call(args);
console.log(params);
if (cache[params]) {
console.log(‘cached’);
return cache[params];
} else {
let result = fn(…args);
cache[params] = result;
console.log(not cached
);
return result;
}
}
}
const makeFullName = (fName, lName) => ${fName} ${lName}
;
const reduceAdd = (numbers, startingValue = 0) => numbers.reduce((total, cur) => total + cur, startingValue);
const memoizedMakeFullName = memoize(makeFullName);
const memoizedReduceAdd = memoize(reduceAdd);
memoizedMakeFullName(“Marko”, “Polo”);
memoizedMakeFullName(“Marko”, “Polo”);
memoizedReduceAdd([1, 2, 3, 4, 5], 5);
memoizedReduceAdd([1, 2, 3, 4, 5], 5);
69. 为什么typeof null 返回 object? 如何检查一个值是否为 null?
typeof null == 'object'
总是返回true
,因为这是自 JS 诞生以来null
的实现。曾经有人提出将typeof null == 'object'
修改为typeof null == 'null'
,但是被拒绝了,因为这将导致更多的bug。
我们可以使用严格相等运算符===
来检查值是否为null
。
function isNull(value){
return value === null;
}
70. new 关键字有什么作用?
new
关键字与构造函数一起使用以创建对象:
function Employee(name, position, yearHired) {
this.name = name;
this.position = position;
this.yearHired = yearHired;
};
const emp = new Employee(“Marko Polo”, “Software Developer”, 2017);
new
关键字做了4
件事:
-
创建空对象
{}
-
将空对象分配给
this
值 -
将空对象的
__proto__
指向构造函数的prototype
-
如果没有使用显式
return
语句,则返回this
看下面事例:
function Person() {
this.name = ‘前端小智’
}
根据上面描述的,new Person()
做了:
-
创建一个空对象:
var obj = {}
-
将空对象分配给
this
值:this = obj -
将空对象的
__proto__
指向构造函数的prototype
:this.__proto__ = Person().prototype
-
返回
this
:return this
71. 什么时候不使用箭头函数? 说出三个或更多的例子?
不应该使用箭头函数一些情况:
-
当想要函数被提升时(箭头函数是匿名的)
-
要在函数中使用
this/arguments
时,由于箭头函数本身不具有this/arguments
,因此它们取决于外部上下文 -
使用命名函数(箭头函数是匿名的)
-
使用函数作为构造函数时(箭头函数没有构造函数)
-
当想在对象字面是以将函数作为属性添加并在其中使用对象时,因为咱们无法访问
this
即对象本身。
72. Object.freeze() 和 const 的区别是什么?]
const
和Object.freeze
是两个完全不同的概念。
const
声明一个只读的变量,一旦声明,常量的值就不可改变:
const person = {
name: “Leonardo”
};
let animal = {
species: “snake”
};
person = animal; // ERROR “person” is read-only
Object.freeze
适用于值,更具体地说,适用于对象值,它使对象不可变,即不能更改其属性。
let person = {
name: “Leonardo”
};
let animal = {
species: “snake”
};
Object.freeze(person);
person.name = “Lima”; //TypeError: Cannot assign to read only property ‘name’ of object
console.log(person);
73. 如何在 JS 中“深冻结”对象?
如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:
没有深冻结
let person = {
name: “Leonardo”,
profession: {
name: “developer”
}
};
Object.freeze(person);
person.profession.name = “doctor”;
console.log(person); //output { name: ‘Leonardo’, profession: { name: ‘doctor’ } }
深冻结
function deepFreeze(object) {
let propNames = Object.getOwnPropertyNames(object);
for (let name of propNames) {
let value = object[name];
object[name] = value && typeof value === “object” ?
总结
面试前要精心做好准备,简历上写的知识点和原理都需要准备好,项目上多想想难点和亮点,这是面试时能和别人不一样的地方。
还有就是表现出自己的谦虚好学,以及对于未来持续进阶的规划,企业招人更偏爱稳定的人。
万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
前端面试题汇总
JavaScript
前端资料汇总