1.什么是 JS 中的数据类型?请列举并简要解释其特点。
答案:
JS 中有 7 种数据类型:number
、string
、boolean
、null
、undefined
、symbol
和 object
。其中,前 6 种为基本数据类型,object 为引用数据类型。number
表示数字类型,string
表示字符串类型,boolean
表示布尔类型,null
表示空对象指针,undefined
表示未定义类型,symbol
表示唯一值类型,object
表示对象类型。
代码演示:
console.log(typeof 123); // 输出 number
console.log(typeof 'hello'); // 输出 string
console.log(typeof true); // 输出 boolean
console.log(typeof null); // 输出 object
console.log(typeof undefined); // 输出 undefined
console.log(typeof Symbol('foo')); // 输出 symbol
console.log(typeof {}); // 输出 object
2.请解释 JS 中的 this 关键字。
答案:
this
是一个关键字,在函数中使用,指向函数的调用者。this
的指向可以由当前调用的方式,即函数的执行上下文决定,通常有以下 4 种:
全局调用:指向全局对象(浏览器中为 window)
函数内部调用:指向全局对象
对象调用:指向调用对象
构造函数调用:指向新创建的对象
代码演示:
console.log(this); // 全局调用,输出 window 对象
function foo() {
console.log(this); // 函数内部调用,输出 window 对象
}
foo();
const obj = {
name: 'Tom',
sayHi() {
console.log(this); // 对象调用,输出 obj 对象
}
};
obj.sayHi();
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this); // 构造函数调用,输出新创建的 person 对象
}
}
const person = new Person('Lucy', 18);
person.sayHi();
3.请用 ES6 的箭头函数实现一个求和函数。
答案:箭头函数是 ES6 引入的一种新的函数定义方式,可以使用更简洁的语法来定义函数,并且默认绑定了当前作用域的 this 对象。
代码演示:
const sum = (a, b) => a + b;
console.log(sum(1, 2)); // 输出 3
4.请用 JS 中的闭包实现一个计数器函数。
答案:闭包是指有权访问另一个函数作用域中变量的函数,可以保护变量不受外部访问和修改。在以下示例中,counter 函数返回了一个内部函数,该内部函数使用了 counter 函数作用域中的变量 count,保证了 count 的私有性和不受外部访问的特点。
代码演示:
function counter() {
let count = 0;
return function () {
count++;
return count;
};
}
const countFunc = counter();
console.log(countFunc()); // 输出 1
console.log(countFunc()); // 输出 2
console.log(countFunc()); // 输出 3
5.请使用 Promise 实现一个异步请求示例。
答案:Promise 是一种处理异步操作的方式,它解决了回调地狱的问题,并且可以很容易地在异步函数之间进行传递和练习。
代码演示:
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = {
success: true,
data: 'Data is loaded successfully!'
}
if (data.success) {
resolve(data);
} else {
reject(new Error('Data load failed'));
}
}, 3000);
});
}
requestData()
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log('Error:', err.message);
});
6.请解释 JS 中的事件委托。
答案:事件委托是一种常用的优化性能的方式。它是通过将事件处理器绑定到其祖先元素而不是每个元素来实现的。事件冒泡会使事件从被点击的元素一直冒泡至祖先元素,因此在祖先元素上使用事件委托可以处理整个元素集的点击事件,而不是为每个元素添加单独的事件处理器。
代码演示:
// HTML 代码
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
// JS 代码
const list = document.querySelector('#list');
list.addEventListener('click', function (event) {
const target = event.target;
if (target.matches('li')) {
console.log('Item clicked:', target.innerText);
}
});
7.请使用 reduce 函数实现一个数组求和示例。
答案:reduce 函数是 ES5 中引入一个用于数组操作的函数,它可以将数组中的所有元素通过指定的函数进行汇总,返回一个值。
代码演示:
const arr = [1, 2, 3, 4, 5];
const sum = arr.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 输出 15
8.请使用 JS 实现一个简易版的 Promise。
答案:Promise 是一种处理异步操作的方式,它解决了回调地狱的问题,并且可以很容易地在异步函数之间进行传递和练习。以下示例是一个简易版本的 Promise,可以帮助理解 Promise 的核心原理。
代码演示:
function MyPromise(fn) {
const _this = this;
_this.state = 'pending';
_this.value = null;
_this.reason = null;
_this.onFulfilledCallbacks = [];
_this.onRejectedCallbacks = [];
function resolve(value) {
if (_this.state === 'pending') {
_this.state = 'fulfilled';
_this.value = value;
_this.onFulfilledCallbacks.forEach(fn => fn());
}
}
function reject(reason) {
if (_this.state === 'pending') {
_this.state = 'rejected';
_this.reason = reason;
_this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const _this = this;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
const promise2 = new MyPromise((resolve, reject) => {
if (_this.state === 'fulfilled') {
setTimeout(() => {
try {
const x = onFulfilled(_this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (_this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(_this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (_this.state === 'pending') {
_this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(_this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
_this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(_this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
9.如何实现JS继承?
答案解析:
JS继承可以通过原型链和构造函数来实现。其中,构造函数实现继承的方式是在子类的构造函数内部调用父类的构造函数,并使用call或apply方法改变this的指向,从而继承父类的属性和方法。原型链实现继承的方式是将子类的原型对象指向父类的实例,从而继承父类的原型链上的属性和方法。以下是实现继承的
代码演示:
// 构造函数继承
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
}
function Student(name, age, grade) {
Person.call(this, name, age); // 调用父类构造函数,并改变this指向
this.grade = grade;
}
// 原型链继承
function Animal() {
this.type = 'animal';
}
Animal.prototype.sayType = function() {
console.log(`I'm a ${this.type}.`);
}
function Dog() {
this.name = 'dog';
}
Dog.prototype = new Animal();
console.log(new Person('Alice', 25));
console.log(new Student('Bob', 18, 'A'));
console.log(new Dog());
10.如何实现JS函数柯里化?
答案解析:
函数柯里化是将一个多参数函数转换成一系列单参数函数的形式。可以通过闭包和递归的方式来实现函数柯里化。以下是实现函数柯里化的
代码演示:
// 闭包方式实现函数柯里化
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
console.log(add(1)(2)(3)); // 6
// 递归方式实现函数柯里化
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
function add2(a, b, c) {
return a + b + c;
}
const curriedAdd2 = curry(add2);
console.log(curriedAdd2(1)(2)(3)); // 6
console.log(curriedAdd2(1, 2)(3)); // 6
console.log(curriedAdd2(1)(2, 3)); // 6
11.如何实现JS函数节流?
答案解析:
函数节流是指一段时间内只执行一次函数,可以通过setTimeout和时间戳来实现。以下是实现函数节流的
代码演示:
function throttle(func, delay) {
let previous = 0;
return function(...args) {
const current = Date.now();
if (current - previous >= delay) {
previous = current;
return func.apply(this, args);
}
}
}
function handleScroll() {
console.log('scrolling...');
}
window.addEventListener('scroll', throttle(handleScroll, 1000));
12.如何实现JS函数防抖?
答案解析:
函数防抖是指在一段时间内多次执行函数时,只有最后一次才真正执行函数,可以通过setTimeout来实现。以下是实现函数防抖的
代码演示:
function debounce(func, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
}
}
function handleInput(event) {
console.log(event.target.value);
}
const debouncedHandleInput = debounce(handleInput, 500);
document.querySelector('input').addEventListener('input', debouncedHandleInput);
13.如何判断一个变量的类型?
答案解析:
可以使用typeof操作符来判断变量类型,它会返回一个字符串表示变量类型。但是对于引用类型来说,typeof会将数组、对象、null等类型都判断为object。可以使用instanceof操作符或constructor属性来判断引用类型的具体类型。以下是判断变量类型的
代码演示:
console.log(typeof undefined); // undefined
console.log(typeof null); // object
console.log(typeof 123); // number
console.log(typeof 'abc'); // string
console.log(typeof true); // boolean
console.log(typeof {}); // object
console.log(typeof []); // object
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(null instanceof Object); // false
console.log((function() {}).constructor === Function); // true
console.log(([]).constructor === Array); // true
console.log(({}).constructor === Object); // true
14.如何将一个字符串转化为日期对象?
答案解析:
可以使用Date对象的构造函数来将字符串转化为日期对象,也可以使用Date.parse()方法来将字符串转化为时间戳,然后使用new Date()来将时间戳转化为日期对象。以下是将字符串转化为日期对象的
代码演示:
console.log(new Date('2023/01/01')); // Mon Jan 02 2023 00:00:00 GMT+0800 (中国标准时间)
console.log(new Date('Mon Jan 02 2023')); // Mon Jan 02 2023 00:00:00 GMT+0800 (中国标准时间)
console.log(new Date('2023/01/01').getTime()); // 1672492800000
console.log(new Date(1672492800000)); // Mon Jan 02 2023 00:00:00 GMT+0800 (中国标准时间)
15.如何实现JS数组去重?
答案解析:
可以使用ES6中的Set数据结构来实现数组去重,也可以使用两重循环或使用indexOf()方法来实现数组去重。以下是实现数组去重的
代码演示:
const arr = [1, 2, 1, 3, 4, 2, 5];
console.log([...new Set(arr)]); // [1, 2, 3, 4, 5]
// 使用两重循环实现数组去重
function unique(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
let isDuplicate = false;
for (let j = 0; j < result.length; j++) {
if (arr[i] === result[j]) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
result.push(arr[i]);
}
}
return result;
}
console.log(unique(arr)); // [1, 2, 3, 4, 5]
// 使用indexOf()方法实现数组去重
function unique2(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
console.log(unique2(arr)); // [1, 2, 3, 4, 5]
16.如何实现JS数组合并?
答案解析:
可以使用concat()方法或扩展运算符来实现数组合并。以下是实现数组合并的
代码演示:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
console.log(arr1.concat(arr2)); // [1, 2, 3, 4, 5, 6]
console.log([...arr1, ...arr2]); // [1, 2, 3, 4, 5, 6]
17.如何实现JS字符串反转?
答案解析:
可以使用split()方法将字符串转化为数组,然后使用reverse()方法反转数组,最后使用join()方法将数组转回字符串。以下是实现字符串反转的
代码演示:
const str = 'hello';
console.log(str.split('').reverse().join('')); // olleh
18.实现一个函数,判断一个字符串是否为回文字符串。
代码演示:
function isPalindrome(str) {
const reversedStr = str.split('').reverse().join('');
return str === reversedStr;
}
19.如何判断一个变量是数组类型?
let arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
Array.isArray()
方法可以用来判断一个变量是否为数组类型。
20.如何将字符串转化为数字类型?
javascript
let str = "123";
let num = Number(str);
console.log(num); // 123
Number()
方法可以将字符串转化为数字类型。
21.如何将数字转化为字符串类型?
let num = 123;
let str = num.toString();
console.log(str); // "123"
toString()
方法可以将数字类型转化为字符串类型。
22.如何将一个字符串反转?
let str = "hello";
let reversedStr = str.split("").reverse().join("");
console.log(reversedStr); // "olleh"
使用 split("")
方法将字符串转化为字符数组,然后使用 reverse()
方法将数组反转,最后使用 join("")
方法将数组转化为字符串。
23.如何判断一个字符串是否为回文字符串?
function isPalindrome(str) {
let reversedStr = str.split("").reverse().join("");
return str === reversedStr;
}
console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("hello")); // false
将字符串反转后与原字符串比较是否相等即可。
24.如何统计一个字符串中每个字符出现的次数?
function countChars(str) {
let obj = {};
for (let i = 0; i < str.length; i++) {
let char = str.charAt(i);
if (obj[char]) {
obj[char]++;
} else {
obj[char] = 1;
}
}
return obj;
}
console.log(countChars("hello")); // {h: 1, e: 1, l: 2, o: 1}
使用对象来存储每个字符出现的次数,遍历字符串时,若字符已存在于对象中,则次数加一,否则次数为1。
25.如何将一个数组去重?
let arr = [1, 2, 3, 3, 4, 4, 5];
let uniqueArr = Array.from(new Set(arr));
console.log(uniqueArr); // [1, 2, 3, 4, 5]
使用 Set
数据结构可以去除数组中的重复项,然后使用 Array.from()
方法将 Set
转化为数组。
26.如何将一个数组按照指定规则排序?
let arr = [
{ name: "Tom", age: 25 },
{ name: "Jack", age: 18 },
{ name: "Mary", age: 30 },
];
arr.sort((a, b) => a.age - b.age);
console.log(arr);
// [
// { name: "Jack", age: 18 },
// { name: "Tom", age: 25 },
// { name: "Mary", age: 30 },
// ]
使用 sort()
方法可以对数组按照指定规则进行排序,传入一个比较函数,返回值为负数时表示 a
在 b
前面,返回值为正数时表示 a
在 b
后面,返回值为0时表示相等。
27.如何将一个二维数组转化为一维数组?
let arr = [[1, 2], [3, 4], [5, 6]];
let flatArr = arr.flat();
console.log(flatArr); // [1, 2, 3, 4, 5, 6]
使用 flat()
方法可以将嵌套数组拉平成一维数组。
28.如何在数组中查找指定元素的位置?
let arr = [1, 2, 3, 4, 5];
let index = arr.indexOf(3);
console.log(index); // 2
使用 indexOf()
方法可以查找指定元素在数组中的位置。
29.如何在数组中删除指定元素?
let arr = [1, 2, 3, 4, 5];
let index = arr.indexOf(3);
if (index !== -1) {
arr.splice(index, 1);
}
console.log(arr); // [1, 2, 4, 5]
使用 indexOf()
方法查找指定元素在数组中的位置,然后使用 splice()
方法删除该元素。
30.如何在数组中添加元素?
let arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
使用 push()
方法可以在数组末尾添加元素。
31.如何在数组中插入元素?
let arr = [1, 2, 4, 5];
let index = arr.indexOf(2);
if (index !== -1) {
arr.splice(index + 1, 0, 3);
}
console.log(arr); // [1, 2, 3, 4, 5]
使用 indexOf()
方法查找插入位置,然后使用 splice()
方法在该位置插入元素。
32.如何获取当前时间?
let now = new Date();
console.log(now); // 当前时间的 Date 对象
使用 Date
构造函数可以创建一个当前时间的 Date
对象。
33.如何将时间格式化为指定格式的字符串?
function formatDate(date, format) {
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
let hour = date.getHours();
let minute = date.getMinutes();
let second = date.getSeconds();
format = format.replace("yyyy", year);
format = format.replace("MM", month < 10 ? "0" + month : month);
format = format.replace("dd", day < 10 ? "0" + day : day);
format = format.replace("HH", hour < 10 ? "0" + hour : hour);
format = format.replace("mm", minute < 10 ? "0" + minute : minute);
format = format.replace("ss", second < 10 ? "0" + second : second);
return format;
}
let now = new Date();
let str = formatDate(now, "yyyy-MM-dd HH:mm:ss");
console.log(str); // "2023-01-01 00:00:00"
使用 Date
对象的各个方法获取时间的各个部分,然后使用字符串替换的方法将格式化字符串中的占位符替换为对应的时间部分。
34.如何创建一个对象?
let obj = {};
obj.name = "Tom";
obj.age = 25;
console.log(obj); // {name: "Tom", age: 25}
使用 {}
或 new Object()
构造函数可以创建一个空对象,然后使用 .
或 []
运算符给对象添加属性和方法。
35.如何删除对象的属性?
let obj = { name: "Tom", age: 25 };
delete obj.age;
console.log(obj); // {name: "Tom"}
使用 delete
关键字可以删除对象的属性。
36.如何判断对象是否包含指定属性?
let obj = { name: "Tom", age: 25 };
console.log("name" in obj); // true
console.log("gender" in obj); // false
使用 in
运算符可以判断对象是否包含指定属性。
37.如何遍历对象的属性?
let obj = { name: "Tom", age: 25 };
for (let key in obj) {
console.log(key + ": " + obj[key]);
}
// name: Tom
// age: 25
使用 for...in
循环可以遍历对象的属性,遍历时,key
变量表示属性名,obj[key]
表示属性值。
38.如何将一个对象转化为 JSON 字符串?
let obj = { name: "Tom", age: 25 };
let jsonStr = JSON.stringify(obj);
console.log(jsonStr); // {"name":"Tom","age":25}
使用 JSON.stringify()
方法可以将一个对象转化为 JSON 格式的字符串。