JavaScript 中 10 个需要掌握基础的问题,一线大厂面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

你还可以这样做:

$(location).attr(‘href’, ‘http://stackoverflow.com’)

3.JavaScript 闭包是如何工作的


闭包是一个函数和对该函数外部作用域的引用(词法环境),词法环境是每个执行上下文(堆栈)的一部分,并且是标识符(即局部变量名称)和值之间的映射。

JavaScript 中的每个函数都维护对其外部词法环境的引用。此引用用于配置调用函数时创建的执行上下文。不管何时调用函数,该引用使函数内的代码能够查看在函数外声明的变量。

在下面的代码中,inner与调用foo时创建的执行上下文的词法环境一起形成一个闭包,并对外部隐藏了变量secret

function foo() {

const secret = Math.trunc(Math.random()*100)

return function inner() {

console.log(The secret number is ${secret}.)

}

}

const f = foo() // secret 不能从foo 外部直接访问

f() // 访问 secret 的唯一办法就是调用 f

换句话说,在JavaScript中,函数带有对私有状态的引用,只有它们(以及在相同的词法环境中声明的任何其他函数)可以访问该私有状态。这个状态对函数的调用者是不可见的,这为数据隐藏和封装提供了一种优秀的机制。

请记住,JavaScript中的函数可以像变量一样传递,这意味着这些功能和状态的对可以在程序中传递:类似于在c++中传递类的实例。

如果JavaScript没有闭包,则必须在函数之间显式传递更多状态,从而使参数列表更长,代码更冗余。

所以,如果你想让一个函数总是能够访问私有状态,你可以使用一个闭包,我们经常想把状态和函数联系起来。例如,在Java或c++中,当你向类添加私有实例变量和方法时,这是将状态与功能关联起来。

在 C 语言和大多数其他编程语言中,函数返回后,由于堆栈被销毁,所有的局部变量都不再可访问。在JavaScript中,如果在另一个函数中声明一个函数,那么外部函数的本地变量在返回后仍然可以访问。这样,在上面的代码中,secret在从foo返回后仍然对函数对象内部可用。

闭包在需要与函数关联的私有状态时非常有用。这是一个非常常见的场景,JavaScript直到2015年才有类语法,它仍然没有私有字段语法,闭包满足了这一需求。

私有实例变量

在下面的事例中,函数 toString 隐藏了 Car 类的一些细节。

function Car(manufacturer, model, year, color) {

return {

toString() {

return ${manufacturer} ${model} (${year}, ${color})

}

}

}

const car = new Car(‘Aston Martin’,‘V8 Vantage’,‘2012’,‘Quantum Silver’)

console.log(car.toString())

函数式编程

在下面的代码中,函数inner隐藏了fnargs

function curry(fn) {

const args = []

return function inner(arg) {

if(args.length === fn.length) return fn(…args)

args.push(arg)

return inner

}

}

function add(a, b) {

return a + b

}

const curriedAdd = curry(add)

console.log(curriedAdd(2)(3)()) // 5

面向事件的编程

在以下代码中,函数onClick隐藏了变量BACKGROUND_COLOR

const $ = document.querySelector.bind(document)

const BACKGROUND_COLOR = ‘rgba(200,200,242,1)’

function onClick() {

$(‘body’).style.background = BACKGROUND_COLOR

}

$(‘button’).addEventListener(‘click’, onClick)

Set background color

模块化

在下面的示例中,所有实现细节都隐藏在一个立即执行的函数表达式中。函数ticktoString隐藏了私有状态和函数,它们需要完成自己的工作。闭包使我们能够模块化和封装我们的代码。

let namespace = {};

(function foo(n) {

let numbers = []

function format(n) {

return Math.trunc(n)

}

function tick() {

numbers.push(Math.random() * 100)

}

function toString() {

return numbers.map(format)

}

n.counter = {

tick,

toString

}

}(namespace))

const counter = namespace.counter

counter.tick()

counter.tick()

console.log(counter.toString())

事例 1:

此示例演示局部变量未在闭包中复制。 闭包保留对原始变量本身的引用。 似乎即使外部函数退出后,堆栈仍在内存中保留。

function foo () {

let x = 42

let inner = function () {

console.log(x)

}

x = x + 1

return inner

}

let f = foo()

f()

事例 2:

在下面的代码中,三种方法logincrementupdate 都在同一词法环境闭包中。

function createObject() {

let x = 42;

return {

log() { console.log(x) },

increment() { x++ },

update(value) { x = value }

}

}

const o = createObject()

o.increment()

o.log() // 43

o.update(5)

o.log() // 5

const p = createObject()

p.log() // 42

事例 3:

如果使用的变量是使用var声明的,需要注意的一点是,使用var声明的变量被提升。 由于引入了letconst,这在现代JavaScript 中几乎没有问题。

在下面的代码中,每次循环中,都会创建一个新的inner函数,变量i被覆盖,但是因var会让 i 提升到函数的顶部,所以所有这些inner函数覆盖的都是同一个变量,这意味着i(3)的最终值被打印了三次。

function foo () {

var result = []

for (var i = 0; i < 3; i++) {

result.push(function inner () {

console.log(i)

})

}

return result

}

const result = foo()

for(var i = 0; i < 3; i++) {

resulti

}

// 3 3 3

最后一点:

  • 每当在JavaScript中声明函数时,都会创建一个闭包。

  • 从一个函数内部返回另一个函数是闭包的经典例子,因为外部函数内部的状态对于返回的内部函数是隐式可用的,即使外部函数已经完成执行。

  • 只要在函数内使用eval(),就会使用一个闭包。eval的文本可以引用函数的局部变量,在非严格模式下,甚至可以通过使用eval('var foo = ')创建新的局部变量。

  • 当在函数内部使用new Function()(Function constructor)时,它不会覆盖其词法环境,而是覆盖全局上下文。新函数不能引用外部函数的局部变量。

  • 在JavaScript中,闭包类似于在函数声明时保留对作用域的引用(而不是复制),后者又保留对其外部作用域的引用,以此类推,一直到作用域链顶端的全局对象。

  • 声明函数时创建一个闭包。 当调用函数时,此闭包用于配置执行上下文。

  • 每次调用函数时都会创建一组新的局部变量。

JavaScript 中的每个函数都维护与其外部词法环境的链接。 词法环境是所有名称的映射(例如,变量,参数)及其范围内的值。因此,只要看到function关键字,函数内部的代码就可以访问在函数外部声明的变量。

function foo(x) {

var tmp = 3;

function bar(y) {

console.log(x + y + (++tmp)); // 16

}

bar(10);

}

foo(2);

上面输出结果是16,参数x和变量tmp都存在于外部函数foo的词法环境中。函数bar及其与函数foo的词法环境的链接是一个闭包。

函数不必返回即可创建闭包。 仅仅凭借其声明,每个函数都会在其封闭的词法环境中关闭,从而形成一个闭包。

function foo(x) {

var tmp = 3;

return function (y) {

console.log(x + y + (++tmp)); // 16

}

}

var bar = foo(2);

bar(10); // 16

bar(10); // 17

上面还是打印16,因为bar内的代码仍然可以引用参数x和变量tmp,即使它们不再直接的作用域内。

但是,由于tmp仍然在bar的闭包内部徘徊,因此可以对其进行递增。 每次调用bar时,它将增加1

闭包最简单的例子是这样的:

var a = 10;

function test() {

console.log(a); // will output 10

console.log(b); // will output 6

}

var b = 6;

test();

当调用一个JavaScript函数时,将创建一个新的执行上下文ec。连同函数参数和目标对象,这个执行上下文还接收到调用执行上下文的词法环境的链接,这意味着在外部词法环境中声明的变量(在上面的例子中,ab)都可以从ec获得。

每个函数都会创建一个闭包,因为每个函数都有与其外部词法环境的链接。

注意,变量本身在闭包中是可见的,而不是副本。

4. use strict 在 JavaScript 中做了什么,背后的原因是什么


引用一些有趣的部分:

严格模式是ECMAScript 5中的一个新特性,它允许我们将程序或函数放置在严格的操作上下文中。这种严格的上下文会防止某些操作被执行,并引发更多异常。

严格模式在很多方面都有帮助:

  • 它捕获了一些常见的编码漏洞,并抛出异常。

  • 当采取相对不安全的操作(例如访问全局对象)时,它可以防止错误或抛出错误。

  • 它禁用令人困惑或考虑不周到的特性。

另外,请注意,我信可以将“strict mode”应用于整个文件,也可以仅将其用于特定函数。

// Non-strict code…

(function(){

“use strict”;

// Define your library strictly…

})();

// Non-strict code…

如果是在混合使用旧代码和新代码的情况,这可能会有所帮助。它有点像在Perl中使用的“use strict”。通过检测更多可能导致损坏的东西,帮助我们减少更多的错误。

现在所有主流浏览器都支持严格模式。

在原生ECMAScript模块(带有importexport语句)和ES6类中,严格模式始终是启用的,不能禁用。

5.如何检查字符串是否包含子字符串?


ECMAScript 6 引入了string .prototype.include

const string = “foo”;

const substring = “oo”;

console.log(string.includes(substring));

不过,IE 不支持 includes。在 CMAScript 5或更早的环境中,使用String.prototype.indexOf。如果找不到子字符串,则返回-1:

var string = “foo”;

var substring = “oo”;

console.log(string.indexOf(substring) !== -1);

为了使其在旧的浏览器中运行,可以使用这种polyfill

if (!String.prototype.includes) {

String.prototype.includes = function(search, start) {

‘use strict’;

if (typeof start !== ‘number’) {

start = 0;

}

if (start + search.length > this.length) {

return false;

} else {

return this.indexOf(search, start) !== -1;

}

};

}

6. var functionName = function() {} 与 function functionName() {}


不同之处在于functionOne是一个函数表达式,因此只在到达这一行时才会定义,而functionTwo是一个函数声明,在它周围的函数或脚本被执行(由于提升)时就定义。

如,函数表达式

// TypeError: functionOne is not a function

functionOne();

var functionOne = function() {

console.log(“Hello!”);

};

函数声明:

// “Hello!”

functionTwo();

function functionTwo() {

console.log(“Hello!”);

}

过去,在不同的浏览器之间,在块中定义的函数声明的处理是不一致的。严格模式(在ES5中引入)解决了这个问题,它将函数声明的范围限定在其封闭的块上。

‘use strict’;

{ // note this block!

function functionThree() {

console.log(“Hello!”);

}

}

functionThree(); // ReferenceError

function abc(){}也具有作用域-名称abc在遇到该定义的作用域中定义。 例:

function xyz(){

function abc(){};

// abc 在这里定义…

}

// …不是在这里

如果想在所有浏览器上给函数起别名,可以这么做:

function abc(){};

var xyz = abc;

在本例中,xyz和abc都是同一个对象的别名

console.log(xyz === abc) // true

它的名称是自动分配的。但是当你定义它的时候

var abc = function(){};

console.log(abc.name); // “”

它的name称为空,我们创建了一个匿名函数并将其分配给某个变量。使用组合样式的另一个很好的理由是使用简短的内部名称来引用自身,同时为外部用户提供一个长而不会冲突的名称:

// 假设 really.long.external.scoped 为 {}

really.long.external.scoped.name = function shortcut(n){

// 它递归地调用自己:

shortcut(n - 1);

// …

// 让它自己作为回调传递::

someFunction(shortcut);

// …

}

在上面的例子中,我们可以对外部名称进行同样的操作,但是这样做太笨拙了(而且速度更慢)。另一种引用自身的方法是arguments.callee,这种写法也相对较长,并且在严格模式中不受支持。

实际上,JavaScript对待这两个语句是不同的。下面是一个函数声明:

function abc(){}

这里的abc可以定义在当前作用域的任何地方:

// 我们可以在这里调用

abc();

// 在这里定义

function abc(){}

// 也可以在这里调用

abc();

此外,尽管有 return 语句,也可以提升:

// 我们可以在这里调用

abc();

return;

function abc(){}

下面是一个函数表达式:

var xyz = function(){};

这里的xyz是从赋值点开始定义的:

// 我们不可以在这里调用

xyz();

// 在这里定义 xyz

xyz = function(){}

// 我们可以在这里调用

xyz();

函数声明与函数表达式之间存在差异的真正原因。

var xyz = function abc(){};

console.log(xyz.name); // “abc”

就个人而言,我们更喜欢使用函数表达式声明,因为这样可以控制可见性。当我们像这样定义函数时:

var abc = function(){};

我们知道,如果我们没有在作用域链的任何地方定义abc,那么我们是在全局作用域内定义的。即使在eval()内部使用,这种类型的定义也具有弹性。而定义:

function abc(){};

取决于上下文,并且可能让你猜测它的实际定义位置,特别是在eval()的情况下,—取决于浏览器。

7.如何从 JavaScript 对象中删除属性?


我们可以这样删除对象的属性:

delete myObject.regex;

// 或者

delete myObject[‘regex’];

// 或者

var prop = “regex”;

delete myObject[prop];

事例:

var myObject = {

“ircEvent”: “PRIVMSG”,

“method”: “newURI”,

“regex”: “^http://.*”

};

delete myObject.regex;

console.log(myObject);

JavaScript 中的对象可以看作键和值之间的映射。delete操作符用于一次删除一个键(通常称为对象属性)。

var obj = {

myProperty: 1

}

console.log(obj.hasOwnProperty(‘myProperty’)) // true

delete obj.myProperty

console.log(obj.hasOwnProperty(‘myProperty’)) // false

delete 操作符不是直接释放内存,它不同于简单地将nullundefined值赋给属性,而是将属性本身从对象中删除。

注意,如果已删除属性的值是引用类型(对象),而程序的另一部分仍然持有对该对象的引用,那么该对象当然不会被垃圾收集,直到对它的所有引用都消失。

delete只对其描述符标记为configurable的属性有效。

8. JS 的比较中应使用哪个等于运算符(== vs ===)?


严格相等运算符(===)的行为与抽象相等运算符(==)相同,除非不进行类型转换,而且类型必须相同才能被认为是相等的。

==运算符会进行类型转换后比较相等性。 ===运算符不会进行转换,因此如果两个值的类型不同,则===只会返回false。

JavaScript有两组相等运算符:===!==,以及它们的孪生兄弟==!=。如果这两个操作数具有相同的类型和相同的值,那么===的结果就是 true,而!==的结果就是 false

下面是一些事例:

‘’ == ‘0’ // false

0 == ‘’ // true

0 == ‘0’ // true

false == ‘false’ // false

false == ‘0’ // true

false == undefined // false

false == null // false

null == undefined // true

’ \t\r\n ’ == 0 // true

上面有些看起来会挺困惑的,所以尽量还是使用严格比较运算符(===)。对于引用类型,=====操作一致(特殊情况除外)。

var a = [1,2,3];

var b = [1,2,3];

var c = { x: 1, y: 2 };

var d = { x: 1, y: 2 };

var e = “text”;

var f = “te” + “xt”;

a == b // false

a === b // false

c == d // false
ES6

  • 列举常用的ES6特性:

  • 箭头函数需要注意哪些地方?

  • let、const、var

  • 拓展:var方式定义的变量有什么样的bug?

  • Set数据结构

  • 拓展:数组去重的方法

  • 箭头函数this的指向。

  • 手写ES6 class继承。

微信小程序

  • 简单描述一下微信小程序的相关文件类型?

  • 你是怎么封装微信小程序的数据请求?

  • 有哪些参数传值的方法?

  • 你使用过哪些方法,来提高微信小程序的应用速度?

  • 小程序和原生App哪个好?

  • 简述微信小程序原理?

  • 分析微信小程序的优劣势

  • 怎么解决小程序的异步请求问题?

其他知识点面试

  • webpack的原理

  • webpack的loader和plugin的区别?

  • 怎么使用webpack对项目进行优化?

  • 防抖、节流

  • 浏览器的缓存机制

  • 描述一下二叉树, 并说明二叉树的几种遍历方式?

  • 项目类问题

  • 笔试编程题:

最后

技术栈比较搭,基本用过的东西都是一模一样的。快手终面喜欢问智力题,校招也是终面问智力题,大家要准备一下一些经典智力题。如果排列组合、概率论这些基础忘了,建议回去补一下。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

var e = “text”;

var f = “te” + “xt”;

a == b // false

a === b // false

c == d // false
ES6

  • 列举常用的ES6特性:

  • 箭头函数需要注意哪些地方?

  • let、const、var

  • 拓展:var方式定义的变量有什么样的bug?

  • Set数据结构

  • 拓展:数组去重的方法

  • 箭头函数this的指向。

  • 手写ES6 class继承。

微信小程序

  • 简单描述一下微信小程序的相关文件类型?

  • 你是怎么封装微信小程序的数据请求?

  • 有哪些参数传值的方法?

  • 你使用过哪些方法,来提高微信小程序的应用速度?

  • 小程序和原生App哪个好?

  • 简述微信小程序原理?

  • 分析微信小程序的优劣势

  • 怎么解决小程序的异步请求问题?

其他知识点面试

  • webpack的原理

  • webpack的loader和plugin的区别?

  • 怎么使用webpack对项目进行优化?

  • 防抖、节流

  • 浏览器的缓存机制

  • 描述一下二叉树, 并说明二叉树的几种遍历方式?

  • 项目类问题

  • 笔试编程题:

最后

技术栈比较搭,基本用过的东西都是一模一样的。快手终面喜欢问智力题,校招也是终面问智力题,大家要准备一下一些经典智力题。如果排列组合、概率论这些基础忘了,建议回去补一下。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-7OHqur2O-1713678646944)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值