前端面试 - JavaScript篇

值类型与引用类型有什么区别

(1) 值类型

StringNumberBooleanSymbol

(2) 引用类型

JSONArraynull

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

undefined 与 null

undefined: “无”原始值 NaN
null: “无”对象0

undefined:

  1. 是一个值,表示已声明但没有值的变量
  2. 类型未定义。
  3. 表示变量不存在。
  4. 使用原始操作转换为 NaN。
// 1.以声明,未赋值
let o;
console.log(o);

// 2.对象某个属性不存在
let obj = {}
console.log(obj.a);

// 3.函数少了参数
function fn(a,b){
	console.log(a,b);
}
fn(4)

// 函数的默认
function abcd(){
	console.log("11");
	return 123;
}
console.log(abcd());

null:

没有值,它突出显示变量不指向任何对象,类型是一个对象

  1. 是一个值,表示变量不指向任何对象。
  2. 是对象类型。
  3. 表示 null、空或不存在的引用。
  4. 表示缺少变量值。
  5. 使用原始操作转换为 0。
  6. 手动释放内存
  7. 作为函数的参数(此参数不是对象)
  8. 原型链的顶端
let obj = {}
obj = null;

isNan() 函数有什么作用?

可以使用 isNan() 函数来检查值的类型是否为数字且为 NaN。

(NaN 是数字类型,尽管它的名称是“非数字”。这是因为它是底层的数字类型,即使它没有数值。)

function toPounds(kilos) {
  if (isNaN(kilos)) {
    return 'Not a Number! Cannot be a weight.';
  }
  return kilos * 2.2;
}

console.log(toPounds('this is a test'));
console.log(toPounds('100'));

//Not a Number! Cannot be a weight.
//220.00000000000003

负无穷大是什么?

如果在 JavaScript 中将负数除以零,则会得到负无穷大。

console.log(-10/0)// -Infinity

什么是未声明变量?未定义的变量怎么样?

程序中根本不存在未声明的变量。如果您的程序试图读取未声明的变量,则会引发运行时错误。

调用未声明变量的示例显然会导致错误:

error: Uncaught ReferenceError: dog is not defined

未定义的变量在程序中声明但没有值。如果程序尝试读取未定义的变量,则会返回未定义的值并且应用程序不会崩溃。

未定义变量的示例是:

let car;
console.log(car);

输出:

undefined

3种弹出框

弹窗的三种类型有警告、确认和提示。让我们看一下每个的使用示例:

1) 警告

window.alert("Hello, world!");

2) 确认

if (window.confirm("Are you sure you want to go?")) {
  window.open("exit.html", "See you again!");
}

3) 提示

let person = window.prompt("Enter your name");
if (person != null) {
  console.log('Hello', person);
}

何时使用 ==,何时使用 ===

== 比较值

=== 比较值和类型

(1) 隐式类型转换

100 ==100// true
0 == ‘’	// true
0 == false	// true
false == ''	// true
null == undefined	// true

隐式类型强制有什么作用?
隐式类型强制意味着一个值在幕后从一种类型转换为另一种类型。当表达式的操作数属于不同类型时,就会发生这种情况。
例如,字符串强制转换意味着在数字上应用 + 运算符,字符串会自动将数字转换为字符串。

var x = 1;
var y = "2";
x + y // Returns "12"

但是在处理减法时,强制以另一种方式起作用。它将字符串转换为数字。

var x = 10;
var y = "10";
x - y // Returns 0

(2) 只有 obj == null 使用双等

if( obj === null || obj === undefined){
	
}

双感叹号有什么作用?

双感叹号将任何东西转换为 JavaScript 中的布尔值。
这是可行的,因为 JavaScript 中的任何东西本质上都是TruthyFalsy

!!true    // true
!!2       // true
!![]      // true
!!"Test"  // true

!!false   // false
!!0       // false
!!""      // false

哪些是 truly 变量?哪些是 falsely 变量?

  • truly 变量:!!val === true
  • falsely 变量:!!val === false

除了 falsely 变量,都是 truely 变量

  • 数字 0
  • NaN
  • 空字符串
  • null
  • undefined

如何检查变量的类型?

使用 typeof 运算符。

typeof "John Abraham"  // Returns "string"
typeof 100             // Returns "number"

元素的样式/类如何改变?

可以通过以下方式改变元素的样式。

document. getElementById("myText").style. fontsize ="20";

可以通过以下方式改变元素的类。

document. getElementById("myText ".className ="anyclass"

循环结构都有哪些?

for、 while、do.… while、 for in、 for of(ES6新增的)

break和 continue语句的作用是什么?

break语句从当前循环中退出;
continue语句继续下一个循环语句。

如何将base字符串转换为 integer?

parselnt()函数解析一个字符串参数,并返回一个指定基数的整数。 parselnt()将要转换的字符串作为其第一个参数,第二个参数是给定字符串的转换进制基数。

为了将4F(基数16)转换为整数,可以使用代码 parrent(“4F”,16)。

自定义 flat 方法拍平数组

(1) Array.concat()

  • Array.concat(1,2,3,[4,5])

(2) 判断是不是数组

  • xxx instanceof Array

(3) 递归

const arr = [1,2,[3,4,[5,6]]];
const res = flat(arr);
function flat(arr){
	while(arr.some((item) ==> item instanceof Array)){
		arr = Array.prototype.concat.apply([],arr);
	}
	return arr;
}

求数组中的最大值

  • Array.sort()
  • Array.reduce()
  • Math.max()
const arr = [100,5,20,3,200,6];
console.log(getMax(arr));

// 方法1:Array.sort()
function getMax(arr){
	arr.sort((n1,n2) => {
		return n2 - n1;
	});
	return arr[0];
}

// 方法2:Array.reduce()
function getMax(arr){
	arr.reduce((n1,n2) => {
		return n1 > n2 ? n1 : n2;
	});
}

// 方法3:Math.max()
function getMax(arr){
	return Math.max.apply(null,arr);
}

forEach() 与 map()

  • forEach()
  1. 没有返回值
  2. 不能用 break 打断
  3. 遍历的是 value
let arr = ['a','b','c']
let res = arr.forEach(element => {
	console.log(element)
	return element + '1'
})
console.log(res)
  • map()
  1. 有返回值(数组)默认 return 是undefined
  2. 接受的参数是一个函数(key,value)
  3. 不能用 break 打断
let arr = ['a','b','c'];
let res = arr.map((value,key) => {
	return value + '1'
})
console.log(res)

递归实现 1~100 求和

// 1 + 2 + 3
function add(num1,num2){
	let num = num1 + num2;
	if(num2 + 1 > 100){
		return num
	}else{
		return add(num,num2 + 1)
	}
}
let sum = add(1,2);
console.log(sum)

5种异步解决方案

(1) 回调

回调简单地理解为一个函数作为参数传递给另一个函数,回调是早期最常用的异步解决方案之一。
回调不一定是异步的,也不直接相关。

function f1(cb) {
  setTimeout(() => {
    cb && cb();
  }, 2000);
}

f1(() => {
  console.log("1");
});

如上,我们在函数 f1 中使用 setTimeout 模拟一个耗时2s的任务,在耗时任务结束时抛出回调,这样我们就可以调用它,让回调函数在耗时结束时执行函数 f1 中的任务。
这样,我们就把同步操作变成了异步操作。f1不会阻塞程序,相当于先执行程序的主要逻辑,推迟执行耗时操作。

回调的优点和缺点
优点:简单,容易理解。
缺点代码不优雅,可读性差,不易维护,耦合度高,层层嵌套造成回调地狱

(2) 事件监听(发布订阅模式)

发布-订阅模式定义了对象之间一对多的依赖关系,这样当一个对象的状态发生变化时,所有依赖它的对象都会得到通知。
我们都使用过发布-订阅模式,例如,如果我们将事件函数绑定到 DOM 节点。

document.body.addEventListener('click', function () {
  console.log('click');
})

但这只是发布-订阅模式最简单的使用,在很多场景下我们往往会使用一些自定义事件来满足我们的需求。
有很多方法可以实现发布-订阅模式,所以这里有一个使用类的简单实现。

class Emitter {
  constructor() {
    // _listener array, key is the custom event name, value is the execution callback array - as there may be more than one
    this._listener = []
  }

  // 订阅 监听事件
  on(type, fn) {
    // Determine if the event exists in the _listener array.
    // Exists to push the callback to the value array corresponding to the event name, does not exist to add directly
    this._listener[type] 
      ? this._listener[type].push(fn) 
     : (this._listener[type] = [fn])
  }

  // Publish Trigger Event
  trigger(type, ...rest) {
    // Determine if the trigger event exists
    if (!this._listener[type]) return
    // Iterate through the array of callbacks executing the event and pass the parameters
    this._listener[type].forEach(callback => callback(...rest))
  }
}

如上所示,我们创建了一个 Emitter 类,并在和触发器上添加了两个原型方法,使用如下。

// Create an emitter instance
const emitter = new Emitter()

emitter.on("done", function(arg1, arg2) {
  console.log(arg1, arg2)
})

emitter.on("done", function(arg1, arg2) {
  console.log(arg2, arg1)
})

function fn1() {
  console.log('I am the main program')
  setTimeout(() => {
    emitter.trigger("done", "Asynchronous parameter I", "Asynchronous parameter II")
  }, 1000)
}

fn1()

我们先创建一个emitter实例,然后注册事件,然后触发事件,这样也解决了异步问题。

事件监听的优点和缺点
优点:更符合模块化思想,我们在编写自己的监听器的时候可以做很多优化,从而更好的监听程序的运行。

缺点:整个程序变成了事件驱动,或多或少影响了流程,而且每次使用都要注册事件监听器然后触发,比较麻烦,代码也不是很优雅。

(3) Promise

ES6 标准化并引入了 Promise 对象,这是一种异步编程的解决方案。
简单的说,就是用同步的方式写异步代码,可以用来解决回调地狱问题
Promise对象的状态一旦改变,就不会再改变,只有两种可能的改变。

  1. 由待定改为已解决。
  2. 由Pending改为Rejected。

我们使用 setTimeout 来模拟异步操作。

function analogAsync(n) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(n + 500), n);
  });
}

function fn1(n) {
  console.log(`step1 with ${n}`);
  return analogAsync(n);
}

function fn2(n) {
  console.log(`step2 with ${n}`);
  return analogAsync(n);
}

function fn3(n) {
  console.log(`step3 with ${n}`);
  return analogAsync(n);
}

使用 Promise 来实现。

function fn() {
  let time1 = 0;
  fn1(time1)
    .then((time2) => fn2(time2))
    .then((time3) => fn3(time3))
    .then((res) => {
      console.log(`result is ${res}`);
    });
}

fn();

Promise 优点和缺点
优点:Promise以同步的方式编写异步代码,避免了回调函数层层嵌套,可读性更强。链式操作,可以在then中继续写Promise对象并return,然后继续调用then进行回调操作。
缺点:Promise对象一旦创建就会立即执行,不能中途取消。如果没有设置回调函数,Promise 会在内部抛出错误,不会向外流。

(4) Generator

Generator其实就是一个函数,只不过是一个特殊的函数。Generator 的特别之处在于它可以中途停止。

function *generatorFn() {
  console.log("a");
  yield '1';
  console.log("b");
  yield '2'; 
  console.log("c");
  return '3';
}

let it = generatorFn();
it.next();
it.next();
it.next();
it.next();

上面的示例是一个具有以下特征的生成器函数。与普通函数不同,Generator 函数在函数之后和函数名称之前有一个 *,该函数有一个内部 yield 字段,函数调用后的返回值使用next方法

Generator的优点和缺点
优点:优雅的流程控制方法,允许函数被中断地执行。
缺点:Generator函数的执行必须依赖executor,对于只做异步处理还是不太方便。

(5) async/await

ES2017标准引入了async函数,使得异步操作更加方便。
async是异步的意思,await是async wait的简写,也就是异步等待。
async/await 被许多人认为是 js 中异步操作的终极和最优雅的解决方案。

异步在做什么?
async 函数返回一个 Promise 对象。如果直接在 async 函数中返回一个直接量,async 会通过 Promise.resolve() 将直接量包装在一个 Promise 对象中。

await 是什么?
await 是一个表达式,其计算结果为 Promise 对象或其他值(换句话说,没有特殊限定,无论如何)。
如果 await 后面没有跟 Promise 对象,则直接执行
如果 await 后面跟着一个 Promise 对象,它会阻塞后面的代码,Promise 对象解析,然后获取 resolve 的值作为 await 表达式的结果。

await 只能在异步函数中使用

上面使用setTimeout来模拟异步操作,我们使用async/await来实现。

async function fn() {
  let time1 = 0;
  let time2 = await fn1(time1);
  let time3 = await fn2(time2);
  let res = await fn3(time3);
  console.log(`result is ${res}`);
}

fn();

输出结果和上面的 Promise 实现是一样的,但是 async/await 的代码结构看起来更清晰,几乎和同步写法一样优雅。

async/await的优点和缺点
优点:内置执行器,语义更好,适用性更广。
缺点误用 await 可能会导致性能问题,因为 await 会阻塞代码

深拷贝

  • JSON.parse(JSON.stringify(obj))
    • 不能存放函数、时间对象、正则…
  • 递归
    • 没有考虑循环引用
  • lodash.cloneDeep // 推荐,使用 lodash插件
    链接:lodash
const obj1 = {
	name:"张三",
	age:18,
	address:{
		city:"北京"
	},
	hobby:["台球","篮球"],
}const obj2 = deepClone(obj1);
obj2.address.city = "上海";
console.log(obj.address.city);	// 北京

// 方法一:
function deepClone(obj){
	return JSON.parse(JSON.stringify(obj))
}

// 方法二:递归s
function deepClone(obj){
	if(typeof obj !== "object" || obj == null){
		return obj;
	}
	let res = obj instanceof Array ? [] : {};
	for(let key in obj){
		// 判断属性是否原型链上的
		if(obj.hasOwnProperty){
			res[key] = deepClone(obj[key])
		}
	}
	return res;
}

闭包的理解

使用闭包主要是为了设计私有的方法和变量,延长变量的生命周期,创建私有环境

它是作用域的一种特殊应用
闭包会常驻内存,都有各自独立的词法作用域

优点:可以避免全局变量的污染;
缺点:闭包会常驻内存,增加内存使用量,使用不当很容易造成内存泄漏。

function a(){
	let a1 = 1;
	return function(){
		return a1;
	}
}

要创建一个自增 1 的计数器,您可以使用闭包:

//createCounter() 是外部函数, increment() 是内部函数。
function createCounter() {
   let counter = 0;
   function increment() {
     counter++;
     console.log(counter);
   }
   return increment;
}

const add = createCounter();
add();	// 1
add();	// 2
add();	// 3

这是有效的,因为存储内部函数 increment() 的 add 仍然可以访问 createCounter() 函数的计数器变量。这是可能的,因为 JavaScript 的闭包特性:即使在外部函数返回后,内部函数也可以访问外部函数的变量。

作用域链:

let name = "小明"
function fn2(){
	let name = "小白"
	function fn3(){
		let name = "小红"
		console.log(name)
	}
	fn3()
}
fn2()

自由变量

不再自己作用域里的变量,就是自由变量
自由变量的值:在函数定义的地方向上层作用域查找,与函数调用无关

触发闭包的情况

  1. 函数当做返回值被返回
  2. 函数当做参数被传递
  3. 自执行匿名函数
// 情况一:函数当做返回值被返回
function fn(){
	const a = 1;
	return function(){
		console.log(a);
	};
}
const a = 5;
const cb = fn();
cb();	// 1

// 情况二:函数当做参数被传递
function fn(cb){
	const a = 100;
	cb();
}
const a = 500;
fn(function (){
	console.log(a);	// 	500
})

// 情况三:自执行匿名函数
(function (index){
	consolelog(index);
})(10);	// 10

闭包的应用

  1. 隐藏变量
  2. 解决 for i 的问题
    在这里插入图片描述
    在这里插入图片描述
let makeCounter = function(){
	let num = 0;
	function changeBy(val){
		num += val;
	}
	return{
		add:function(){
			changeBy(1);
		},
		reduce(){
			changeBy(-1);
		},
		value:function(){
			return num;
		}
	}
}
let counter1 = makeCounter()
let counter2 = makeCounter()
counter1.add()
counter1.add()
counter2.add()
console.log(counter1.value())	// 2
console.log(counter2.value())	// 1

防抖与节流

防抖:将多次操作变成一次(固定时间内,事件只允许发生一次)

<input placeholder="请输入电话"/>
<script>
	let telInput = document.querySelector('input');
	
	// 一:未使用防抖:导致每输入一个字符发一次请求
	telInput.addEventListener('input',(e) => {
		console.log("发起请求")
	});	
	
	// 二:使用防抖封装
	telInput.addEventListener('input',antiShake(demo,2000));	// 当输入停止时,每2秒请求一次
	
	// 防抖封装
	function antiShake(fn,timeAwit){
		let timeOut = null;
		// 闭包
		return args => {
			if(timeOut) clearTimeout(timeOut)
			timeOut = setTimeout(fn,timeAwit);
		}
	}
	function demo(){
		console.log("发起请求")
	}
</script>

节流:一定时间内只调用一次函数(一定时间内的多个事件合为一个)

应用场景:

  1. 提交表单
  2. 高频的监听事件
<div class="box" style="width:100px;height:100px;background-color:red;"></div>
<script>
	let box = document.querySelector('.box');
	
	// 未使用节流:导致用户每触摸一次屏幕就触发一次请求
	box.addEventListener('touchmove',(e) => {
		console.log("发起请求")
	});	
	
	// 使用节流封装
	box.addEventListener('touchmove',throttle(demo,2000));	// 当用户触摸时,每2秒请求一次
	
	// 节流封装
	function throttle(event,time){
		let timer = null;
		// 闭包
		return function() {
			if(!timer){
				timer = setTimeout(() => {
					event();
					timer = null;
				},time);
		}
	}
	function demo(){
		console.log("发起请求")
	}

	// 通过时间戳
</script>

原型与原型链

原型 prototype (函数特有)

原型链 _proro_ ([[prototype]])

从当前实例属性去查找,如果找了就返回,否则顺着原型链一层一层往上查找
直到找到 null 为止,如果找到 null 都没有找到,就会报错

function Person(){

}
Person.prototype.name = '小明'
Person.prototype.age = 18
Person.prototype.getAge = function(){
	console.log(this.age)
}
// 实例
// 从当前实例属性去查找,如果找了就返回,否则顺着原型链一层一层往上查找
let person1 = new Person()
person1.age = 28
console.log(person1)
person1.getAge()

// 找自身的属性
let item;
for(item in person1){
	if(person1.hasOwnPrototype(item)){
		console.log(item)
	}
}

在这里插入图片描述

如何处理异常?

// 用于处理 JavaScript中的异常。 
try...catch...finally 
try{
	执行代码
}catch(exp){
 	抛出错误提示信息
}finally {
 	无论try/catch的结果如何都会执行。
}

如果表达式抛出错误,您可以使用 try…catch 语句处理它们。

function weekDay(dayNum) {
  if (dayNum < 1 || dayNum > 7) {
    throw 'InvalidDayNumber'
  } else {
    return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][dayNum - 1];
  }
}

try { // Try to run the following
  let day = weekDay(8);
  console.log(day);
}
catch (e) { // catch an error if the above try failed
  let day = 'unknown';
  console.log(e);
}

如何实现浏览器内多个标签页之间的通信?

调用 localstorge、 cookie等数据存储通信方式

什么是网络存储?

Web 存储是一种 API,它为浏览器提供了一种将键值对存储到用户浏览器本地的方法。使用网络存储使这个过程比使用 cookie 更直观。

Web 存储提供了两种存储数据的方式:

本地存储——为客户端存储没有过期日期的数据。

会话存储——只存储一个会话的数据,浏览器关闭时数据消失。

以下是如何从 sessionStorage 保存、访问和删除项目的示例:

// Save data to sessionStorage
sessionStorage.setItem('favoriteColor', 'gray');

// Get the color from the sessionStorage
let data = sessionStorage.getItem('favoriteColor');
console.log(data);

// Remove saved color preset from sessionStorage
sessionStorage.removeItem('favoriteColor');

// Remove ALL the saved data from sessionStorage
sessionStorage.clear();

以下是使用 localStorage 执行相同操作的方法:

// Save data to localStorage
localStorage.setItem('favoriteColor', 'gray');

// Get the color from the localStorage
let data = localStorage.getItem('favoriteColor');
console.log(data);

// Remove saved color preset from localStorage
localStorage.removeItem('favoriteColor');

// Remove ALL the saved data from localStorage
localStorage.clear();

为什么需要网络存储?

Web 存储使得在本地存储大量数据成为可能。关键是它不会影响网站的性能。
使用网络存储,信息不会存储到服务器中。与 cookie 相比,这使其成为一种更可取的方法。

高阶函数是什么?

高阶函数对另一个函数进行操作。它要么接受一个函数作为参数,要么返回另一个函数。

function runThis(inputFunction) {
  inputFunction();
}
runThis(function() { console.log("Hello world") });
function giveFunction() {
  return function() {
    console.log("Hello world")
  }
}
var action = giveFunction();
action()

“this”关键字是什么?

指对象本身。

var student = {
    name:  "Matt",
    getName: function(){
      console.log(this.name);
    }
}

student.getName();

call()方法有什么作用?

  1. call() 方法可用于在另一个对象上调用一个对象的方法。
obj1.func.call(obj2)
var student = {
  name: "Matt",
  getName: function(){
    console.log(this.name);
  }
}

var anotherStudent = {
  name: "Sophie"
};

student.getName.call(anotherStudent);	// Sofie
  1. Call() 方法也可用于通过指定所有者对象来调用函数。
function sayHi(){
  console.log("Hello " + this.name);
}

var person = {name: "Matt"};

sayHi.call(person);	// Hello Matt
  1. call() 也可以接受参数。
function sayHi(adjective){
  console.log("Hello " + this.name + ", You are " + adjective);
}

var obj = {name: "Matt"};

sayHi.call(obj, "awesome"); // Hello Matt, you are awesome

什么是apply()方法?

apply() 方法与 call() 方法的作用相同,不同之处在于 apply() 方法接受参数作为数组。

const person = {
    name: 'John'
}

//  greet() 函数中,“Hello”被分配给问候语,“How are you?”被分配给消息。
function greet(greeting, message) {
    return `${greeting} ${this.name}. ${message}`;
}

let result = greet.apply(person, ['Hello', 'How are you?']);

console.log(result);	// Hello John. How are you?

什么是bind()方法?

bind() 方法返回一个新函数,其 this 已设置为另一个对象。

apply() call() 不同,bind() 不会立即执行函数。相反,它返回一个新版本的函数,其 this 被设置为另一个值。

let person = {
    name: 'John',
    getName: function() {
        console.log(this.name);
    }
};

window.setTimeout(person.getName, 1000);	// undefined

// 重写上一行,setTimeout() 接收与人对象分开的函数,但没有人的名字。因此,当 setTimeout() 调用 person.getName 时,名称是未定义的。
let func = person.getName;
setTimeout(func, 1000);

// 重写上一行,将 getName() 方法绑定到 person 对象:
let func = person.getName.bind(person);
setTimeout(func, 1000);	//John

person.getName 方法绑定到 person 对象。

绑定函数 func 现在将 this 值设置为 person 对象。当您将这个新的绑定函数传递给 setTimeout() 函数时,它知道如何获取此人的姓名。

什么是柯里化?

柯里化意味着将具有 n 个参数的函数转换为具有一个或更少参数的 n 个函数。

柯里化不会改变函数的行为,它改变了它被调用的方式

例如,假设您有一个函数 add() 可以对两个数字求和:

function add(a, b) {
    return a + b;
}

// 调用
add(2,3)

然后柯里化函数:

function add(a) {
  return function(b) {
    return a + b;
  }
}

// 调用柯里化函数
add(2)(3)

回调函数是什么?

回调函数是作为参数传递给另一个函数的函数。
当某些动作完成时,此函数在传递给它的函数内部执行以“回调”。

function greetName(name) {
  console.log('Hello ' + name);
}

function askName(callback) {
  let name = prompt('Enter your name.');
  callback(name);
}

askName(greetName);

为什么要使用回调?

因为 JavaScript 是一种事件驱动的语言。换句话说,它不是等待响应,而是在监听其他事件的同时继续执行。

function greetName(name) {
  console.log('Hello ' + name);
}
function askName(callback) {
  let name = prompt('Enter your name.');
  callback(name);
}
askName(greetName);

什么是立即调用函数?

立即调用函数 (IIFE) 在定义后立即运行。

(function(){ 
  // action here
})();

要了解 IIFE 的工作原理,请查看它周围的括号:

  • 当 JavaScript 看到关键字 function 时,它假设有一个函数声明来了。
  • 但是上面的声明是无效的,因为函数没有名字。
  • 为了解决这个问题,使用了声明周围的第一组括号,这告诉解释器这是一个函数表达式,而不是声明。
  • 要调用该函数,需要在函数声明的末尾添加另一组括号,这类似于调用任何其他函数:

什么是构造函数?它与普通函数有什么区别?

构造函数是一种特殊的方法,主要用来创建对象时初始化对象,经常与new运算符一起使用,创建对象的语句中构造函数的名称必须与类名完全相同。
与普通函数相比,区别如下

  1. 构造函数只能由new关键字调用
  2. 构造函数可以创建实例化对象
  3. 构造函数是类的标志。

什么是cookie?

Cookie 是存储在计算机上的一个小数据包。

例如,网站可以在访问者的浏览器上放置 cookie,以便在用户下次访问该页面时记住登录凭据。

在幕后,cookie 是带有键值对的文本文件。要创建、读取或删除 cookie,请使用 document.cookie 属性。

例如,创建一个保存用户名的 cookie:

document.cookie = "username=foobar123";

如何删除属性及其值?

您可以使用 delete 关键字从对象中删除属性及其值。

var student = {name: "John", age:20};
delete student.age;

console.log(student);	// {name: "John"}

用 JavaScript 访问历史记录

通过包含浏览器历史记录的 window.history 访问历史记录。
要检索上一个和下一个 URL,可以使用以下方法:

window.history.back()
window.history.forward()

什么是 JavaScript 事件?

事件是 HTML 元素发生的事情。当在 HTML 页面中使用 JavaScript 时,它可以对事件做出反应,例如,单击按钮。

创建一个 HTML 页面,其中有一个按钮,当该按钮被单击时,会出现一个警告:

<html>
 <head>
   <script>
     function sayHi() {
       alert('Hi, how are you?');
     }
	</script>
 </head>

 <body>
   <button type="button" onclick="sayHi()">Click here</button>
 </body>
</html>

preventDefault() 方法有什么作用?

它阻止事件采用默认行为。

例如,在单击提交按钮时阻止表单提交:

document.getElementById("link").addEventListener("click", function(event){
  event.preventDefault()
});

setTimeout() 与 setInterval()

setTimeout() 方法在指定的毫秒数后调用一个函数(一次)。例如,在一秒(1000 毫秒)后记录一条消息:

setTimeout(function() {
    console.log("Good day");
}, 1000);

setInterval() 方法以自定义间隔定期调用函数。

例如,每秒定期记录一条消息:

setInterval(function() {
    console.log("Good day");
}, 1000);

什么是JSON?

JSON(JavaScript Object Notation)是一种用于交换数据的轻量级数据格式。

语法规则是:

  • 数据以键值对的形式存在。
  • 数据以逗号分隔。
  • 大括号定义一个对象。
  • 方括号定义一个数组。
{
    'name': 'Matt',
    'address': 'Imaginary Road 22',
    'age': 32,
    'married': false,
    'hobbies': ['Jogging', 'Tennis', 'Padel']
}

JSON用在什么地方?

当向服务器发送数据和向服务器发送数据时,数据必须是文本格式。

JSON 是一种纯文本格式,允许将数据发送到服务器以及从服务器向浏览器发送数据。几乎所有编程语言都支持 JSON,因此它也可以与其他语言一起使用。

为什么要使用JSON.stringify?

当您向服务器发送数据时,它必须是一个字符串。

要将 JavaScript 对象转换为字符串,可以使用 JSON.stringify() 方法。

var dataJSON = {name: "Matt", age: 51};
var dataString = JSON.stringify(dataJSON);
console.log(dataString); 	// '{"name":"Matt","age":51}'

如何将 JSON 字符串转换为 JSON 对象?

当您从服务器接收数据时,它始终是字符串格式。要将 JSON 字符串转换为 JavaScript 对象,请使用 JSON.parse() 方法。

var data = '{"name":"Matt", "age":51}';
var dataJSON = JSON.parse(data);
console.log(dataJSON);

{
name:“Matt”,
age:51
}

如何为变量分配默认值?

使用逻辑运算符 || 在分配中提供默认值。

// 这使得如果 b 是假的,那么 c 将被分配给 a。(Falsy 表示 null、false、undefined、0、空字符串或 NaN。)
const a = b || c;

你能为函数定义属性吗?

let func = function(x) {

};
func.property1 = "Hello there";
console.log(func.property1);	// Hello there

解释 pop()、push、unshift()方法。

  1. pop()方法与shift()方法类似,但不同之处在于shift()方法在数组的开头工作。此外,pop()方法将最后一个元素从给定的数组中取出并返回,然后改变被调用的数组例如:
var colors = ["red""blue""green"];
 colors. pop ()// colors :["red","blue"]
  1. push方法用于将一个或多个元素添加或附加到数组的末尾。使用这种方法,可通过传递多个参数来附加多个元素。

  2. unshift()在数组启动时起作用,与 push()不同。它将参数成员添加到数组的顶部下面给出一段示例代。

var name=["john"]
name. unshift("charlie");
name.unshift("joseph""Jane");
console. log(name);	// [" joseph ", Jane "," charlie "," john "]

encodeR() 和 decodeR() 的作用是什么?

encodeURI()用于将URL转换为十六进制编码。
decodeURI()用于将编码的URL转换回正常URL。

callee和 caller的作用是什么?

caller返回一个关于函数的引用,该函数调用了当前函数;
callee返回正在执行的函数,也就是指定的 function对象的正文

为什么不建议在 JavaScript中使用 innerHTML?

通过 innerHTML修改内容,每次都会刷新,因此很慢。
innerHTML中没有验证的机会,因此更容易在文档中插入错误代码,使网页不稳定。

有哪些垃圾回收机制?

(1) 标记清除( mark and sweep)

最常见
当变量进入执行环境的时候,比如在函数中声明一个变量,垃圾回收器将其标记为“进入环境”。当变量离开环境的时候(函数执行结束),将其标记为“离开环境”。
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量,以及被环境中变量所引用的变量(闭包)的标记。在完成这些之后仍然存在的标记就是要删除的变量。

(2) 引用计数( reference counting)

在低版本的E中经常会发生内存泄漏,很多时候就是因为它采用引用计数的方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数。

当声明了一个变量并将个引用类型赋值给该变量的时候,这个值的引用次数就加1.如果该变量的值变成了另外一个,则这个值的引用次数减1.当这个值的引用次数变为0的时候,说明没有变量在使用,这个值没法被访问。

因此,可以将它占用的空间回收,这样垃圾回收器会在运行的时候清理引用次数为0的值占用的空间在正中虽然 JavaScript对象通过标记清除的方式进行垃圾回收,但是BOM与DOM对象是用引用计数的方式回收垃圾的。

也就是说,只要涉及BOM和DOM,就会出现循环引用问题

列举几种类型的DOM节点

  • 整个文档是一个文档( Document)节点。
  • 每个HTML标签是一个元素( Element)节点。
  • 每一个HTML属性是一个属性( Attribute)节点。
  • 包含在HTML元素中的文本是文本(Text)节点。

在DOM操作中怎样创建、添加、移除、替换、插入和查找节点?

  1. 通过以下代码创建新节点。
createDocument Fragment ()
//创建一个D0M片段
createElement ()
//创建一个具体的元素
createTextNode ()
//创建一个文本节点
  1. 通过以下代码添加、移除、替换、插入节点
appendchild()
removechild()
eplacechild ()
insertBefore ()
//并没有 insertAfter()
  1. 通过以下代码查找节点。
getElementsByTagName ()
//通过标签名称查找节点
getElementsByName ()
//通过元素的name属性的值查找节点(IE容错能力较强,会得到一个数//组,其中包括id等于name值的节点)
getElementById(
//通过元素Id查找节点,具有唯一性

对于元素, attribute和 property的区别是什么?

attribute是DOM元素在文档中作为HTML标签拥有的属性;property就是DOM元素在 JavaScript中作为对象拥有的属性。

对于HTML的标准属性来说, attribute和 property是同步的,会自动更新,但是对于自定义的属性来说,它们是不同步的。

documen.wrte和 innerHTML的区别是什么?

document.wite重绘整个页面;
innerHTML可以重绘页面的一部分。

延迟加载的方式有哪些?

包括 deferasync动态创建DOM(创建 script,插入DOM中,加载完毕后回调、按需异步载入 JavaScript。

谈谈 script标签中 defer和 async属性的区别。

  1. defer属性规定是否延迟执行脚本,直到页面加载为止, async属性规定脚本一旦可用,就异步执行。

  2. defer并行加载 JavaScript文件,会按照页面上 script标签的顺序执行, async并行加载 JavaScript文件,下载完成立即执行,不会按照页面上 script标签的顺序执行。

new操作符的作用是什么?

  1. 创建一个空对象。
  2. 由this变量引用该对象
  3. 该对象继承该函数的原型(更改原型链的指向)
  4. 把属性和方法加入到this引用的对象中。
  5. 新创建的对象由this引用,最后隐式地返回this,过程如下:
var obj ={};
obj._ _ proto_ _ Base .prototype;
 Base .call(obj);

JavaScript对象的几种创建方式。

(1) Object构造函数式。
(2)对象字面量式。
(3)工厂模式。
(4)安全工厂模式。
(5)构造函数模式。
(6)原型模式。
(7)混合构造函数和原型模式。
(8)动态原型模式。
(9)寄生构造函数模式。
(10)稳妥构造函数模式。

如何判断一个对象是否属于某个类?

使用 instanceof关键字,判断一个对象是否是类的实例化对象;
使用 constructor属性,判断一个对象是否是类的构造函数。

请解释同源策略,为什么要有?

同源策略指的是协议、域名、端口相同。同源策略是一种安全协议。指一段脚本只能读取来自同一来源的窗口和文档的属性。

如果不用的话,黑客可以通过 Javascript读取到用户表单上 Input中的内容,这样就会轻松得到你的用户名和密码。

读取文件的方法是什么?

读取服务器中的文件内容:

function readAjaxEile(url) {
	//创建xhr 
	var xhr =new XMLHttpRequest();
	//监听状态
	xhr. onreadystatechange=function(){
	//监听状态值是4
	if(xhr. readystate == 4 && xhr. status = = =200{
		console. log(xhr. responseText)
	}
	//打开请求
	xhr.open('GET', url, true//发送数据
	xhr, send(null}

读取本地计算机中的内容:

function readInputFile(id) {
	var file= document. getElementById(id). files[0]//实例化 FileReader 
	var reader=new FileReader();
	//读取文件
	reader. readAsText (file)
	//监听返回
	reader, onload= function (data) {
 		console. log (data, this .result)
	}
}

编写一个方法,求一个字符串的长度(单位是字节)

function GetBytes(str){
 var len=str .length;
 var bytes= len;
 forvar i-0:i<len;1++{
	if (str. charcodeAt (i)>255) bytes++}
 return bytes;
}
alert( GetBytes("hello 有课前端网!"));

数组快速排序

  1. 在数据集之中,选择一个元素作为“基准”( pivot)。
  2. 将所有小于“基准”的元素,都移到“基准”的左边;将所有大于“基准”的元素,都移到“基准”的右边。
  3. 对“基准”左边和右边的两个子集,不断重复第1步和第2步,直到所有子集只剩下一个元素为止。

实现一个数组合并的方法(要求去重)

var arrl =['a']; 
var arr2 =['b''c'];
var arr3=['c', ['d'], 'e'undefined, null];
var concat =function() {
	//去重合并arr1和arr2
	var _concat =function (arrl, arr2)
	forvar i =0, len= arr2.length; i< len; i++){
		arrl.indexOf(arr2[i])|| arrl. push(arr2[i])
	}
}
//返回数组去重合并方法
return function(){
 	var result =[];
	for (var i=0, len= arguments .length;i< len:i++){
 		_concat(result, arguments [i])
		return result
	}
})()

concat(arrl,ar2,ar3);	//['a',null, undefined,'e',['d],'c','b']

如何统计字符串“ aaaabbbccccddfgh”中字母的个数或统计最多的字母数?

var str =aaaabbbecccddfgh";
function dealstr(str){
	var obj={}forvar i= 0;i< str length:i++{
		var v=str.charAt (i);
		if (obj[v] && obj [v].value === v){
			++obj[v]. count 
		} else {
 			obj[v] ={
				count:1va⊥ue:v
			}
		}
	}
 	return obj ;
}

var obj= dealstr(str);
for (key in obj){
 	console. log (obj[key] .value +'=' obj[ key].count)
}

写一个 function,清除字符串前后的空格(兼容所有浏览器)。

function trim(str){
	if (str && typeof str === "string"{ 
		return str.replace(/^\s+1\s+$/g,"");//去除前后空白符。
	}
}

正则表达式给所有string对象添加去除首尾空白符的方法(trim方法)

prototype. trim= function(){
	return this .replace(/^\s+I\s+$/g," );
}

实现一个提取电话号码的方法。

var str="12345678901 021-12345678 有课前端网 0418-1234567  13112345678"var reg=/1\d{0}|0\d{2,3}\-\d{7,8}/g;
alert(str.match(reg);	// [12345678901,021-12345678,0418-1234567,13112345678]

JavaScript中不同类型的错误有几种?

  1. Load time errors,该错误发生于加载网页时,例如出现语法错误等状况,称为加载时间错误,并且会动态生成错误。
  2. Run time errors,由于在HTML语言中滥用命令而导致的错误。
  3. Logical errors,这是由于在具有不同操作的函数上执行了错误逻辑而发生的错误。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qianmo_er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值