整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

从jQuery转向Vue

从jQuery转向Vue

为一个使用了jQuery很多年 的人,最近,我成为了一个Vue的皈依者,我认为从一个框架到另一个框架的迁移过程将是一个值得讨论的有趣的话题。

在我开始之前,我想清楚地说明一点。我并没有以任何方式告诉任何人去停止使用jQuery。jQuery最近相当流行,而且见鬼,我几年前也写过类似的东西(“我如何(不)使用jQuery”)。如果你使用jQuery完成了你的项目,并且你的最终用户成功地使用了你的站点,那么你将获得更多的动力去继续使用对你有用的东西。

本指南更适合那些可能具有多年jQuery经验并希望了解如何使用Vue来完成工作的人。考虑到这一点,我将重点介绍我所认为的“核心”jQuery用例。我不会涵盖每一个可能的特性,而是用“我经常使用jQuery来完成 [X]”的方式来代替,这种方式可能更适合那些考虑学习Vue的人。(顺便提一句,请注意,我编写示例的方式只是执行一个任务的一种方式。jQuery和Vue都提供了多种方法来实现相同的目标,这是一件很棒的事情!)

记住了这一点,我们来思考一些可以使用jQuery完成的高级的东西:

  • 在DOM中找到某些东西(稍后再用它做一些事情)
  • 修改DOM中的某些东西(例如一个段落的文本或一个按钮的类)
  • 读取和设置表单值
  • 表单验证(实际上是上面各项的一个组合)
  • ajax调用和处理结果
  • 事件处理(例如点击按钮,做某些事情)
  • 测量或改变元素的样式

当然,jQuery还有更多的功能,但是这些用途(至少在我看来)涵盖了最常见的用例。还要注意,在上面的列表中有很多异花授粉现象。那么,我们应该从简单的一一对应的比较开始吗?不,没那么快。我们先从介绍Vue应用程序中的主要差异开始。

#定义Vue的使用场景

当我们将jQuery加入到页面上时,我们基本上是在JavaScript代码中添加一把瑞士军刀来处理常见的web开发任务。我们可以按照我们认为合适的顺序来处理任何一个用例。例如,今天客户可能会要求表单验证,然后在一个月左右后,又要求在站点的头部添加一个基于Ajax的搜索表单。

Vue在这方面有一个显著的不同。当使用Vue开始一个项目时,我们首先会在DOM中定义一个我们希望Vue专用的“区域”。因此,我们来考虑一个简单的原型web页面:

在一个典型的jQuery应用程序中,我们可以编写代码来处理头部、侧边栏和登录表单等。这很简单:

而在一个Vue应用程序中,我们首先需要指定要处理的内容。假设我们的客户首先要求我们向loginForm元素添加验证,那么我们的Vue代码就要指定这一点:

这意味着,如果客户后来决定让我们在侧边栏中添加一些内容,那我们通常会添加第二个Vue应用程序:

这是件坏事吗?绝对不是。我们马上就会得到封装的好处。如果我们不小心使用了一个具有泛型名称的变量(我们都这样做过),我们不必担心它与代码的其他部分发生冲突。过后,当客户端增加了另一个要求时,像这样将我们独特的、逻辑化的Vue代码集区分开就会确保每一个Vue应用程序不会相互干扰。

所以,是的,这是一件好事。但当我第一次开始使用Vue时,它绝对让我停了下来。现在,进入我们的用例。

#在DOM中查找东西

你会发现另一个有趣或可怕的方面是如何“在DOM中查找东西”。这有点模糊,但我们来考虑一个强有力的例子。我们有一个按钮,当它被点击时,我们让一些事情发生。下面是一个简短的例子,展示了它是怎样的:

现在我们来将这个例子与用Vue的实现方式进行比较:

这个Vue应用程序有点冗长,但是请注意标记是如何在操作(“click”)和将要调用的函数之间建立一个直接连接的。Vue的代码并没有与DOM进行向后绑定(我们在el部分之外定义了它需要运行的地方)。这是Vue最吸引我的地方之一——它能很容易地告诉你将要发生什么。此外,我不需要过多地担心ID值和选择器。如果我更改了按钮的类或ID,我不需要返回代码中去更新选择器。

我们来考虑另一个例子:在DOM中查找和更改文本。想象一下那个按钮,单击它,现在会更改DOM的另一部分的文本。

我已经添加了一个新的span,现在,当按钮被单击时,我们使用另一个选择器来查找它,并使用一个jQuery工具方法来更改其中的文本。现在我们来考虑一下Vue版本:

在本例中,我们使用Vue的模板语言(突出显示的行)来指定我们希望在span中呈现的一个变量,在本例中是resultText。现在,当按钮被单击时,我们更改该值,span的内部文本将会自动更改。

顺便说一句,Vue支持v-on属性的简写,因此示例中的按钮可以用@click=“ doSomething"代替。

#读写表单变量

处理表单可能是我们可以用JavaScript做的最常见也是最有用的事情之一。甚至在JavaScript之前,我早期的“web开发”大部分都是通过编写Perl脚本来处理表单提交。作为接受用户输入的主要方式,表单对web来说一直都是很重要的,而且很可能会在相当长一段时间内保持不变。我们来考虑一个简单的jQuery例子,它将读取几个表单字段并设置另一个:

这段代码演示了jQuery如何通过val( )方法读写表单。最后,我们从DOM中获取四个项目(所有的三个表单字段和一个按钮),并使用简单的数学方法来生成一个结果。现在我们来考虑一下Vue版本:

这里介绍了一些有趣的Vue快捷方法。首先,v-model是Vue如何在DOM和JavaScript中的值之间创建双向数据绑定。data块变量将自动与表单字段同步。更改数据,表单就会更新。更改表单,数据就会更新。.number是Vue的一个标志,用于将表单字段的继承字符串值视为数字。如果我们不做这一步,按原样做加法,我们会看到字符串加法,而不是算术。我已经使用JavaScript处理了将近一个世纪了,但还是搞砸了。

另一个简单的“技巧”是@click.prevent。首先,@click为按钮定义了一个单击处理程序,然后.prevent部分会阻止浏览器提交表单的默认行为(相当于event.preventDefault( ))。

最后一个是绑定到该按钮的doSum方法进行的相加操作。注意,它只处理数据变量(Vue在this作用域内允许对这些变量进行操作)。

虽然这主要是我个人的感觉,但我非常喜欢在用Vue编写脚本时,脚本中没有查询选择器,以及HTML如何更清楚地显示它在做什么。

最后,我们甚至可以完全去掉按钮:

Vue的一个更酷的特性是computed properties(计算属性)。它们是虚拟值,可以识别其派生值何时被更新。在上面的代码中,只要两个表单字段中的任何一个发生更改,总和就会更新。这也适用于表单字段之外。我们可以这样渲染其总和:

#使用Ajax

值得称赞的是,jQuery使Ajax的使用变得非常简单。事实上,我可以说我已经以一种“普通”的方式完成了Ajax,可能总共只有一次(如果你对此很好奇,你可以查看XMLHttpRequest规范,并且你可能会为你已经避免了它而感到高兴)。jQuery简单的$.get(…)方法在很多情况下都能工作,并且当它需要在更复杂的东西中使用时,$.ajax()也能使它变得简单。jQuery做得很好的另一件事是它处理JSONP请求的方式。虽然现在使用CORS基本上没有必要,但JSONP是一种处理向不同域中的API发出请求的方法。

那么,Vue如何让Ajax变得更简单呢?没有什么!

好吧,听起来很吓人,但其实并不可怕。有许多处理HTTP请求的选项,而Vue.js采用了一种更不可知的方式,让我们开发人员决定如何处理它。所以,是的,这确实意味着更多的工作,但我们有一些不错的选择。

首先应该考虑的是Axios,这是一个Promise-based库,在Vue社区中非常流行。下面是一个使用它的简单的例子(摘自它们的README文件):

Axios支持POST请求,当然,它也允许我们在许多其他选项中指定头文件。

虽然Axios在Vue开发人员中非常流行,但我并不是真心喜欢它。(至少现在还没有。)相反,我更喜欢Fetch。Fetch不是一个外部库,而是执行HTTP请求的一种web标准方法。Fetch在大约90%的浏览器

上都有很好的支持,虽然这意味着使用它并不完全安全,但是我们总是可以使用一个我们需要的polyfill。

虽然这完全超出了我们在这里讨论的范围,但是Kingsley Silas写了一本关于在React中使用Axios和Fetch的优秀指南。

和Axios一样,Fetch也是Promise-based的,并且有一个友好的API:

Axios和Fetch都涵盖了所有类型的HTTP请求,所以它们都能满足任意数量的需求。让我们看一个简单的比较。下面是一个使用了星球大战API的简单jQuery演示。

在上面的示例中,我使用$.get调用该API并返回一个电影列表。然后我用这些数据生成一个标题列表作为 li 标记元素,并将其全部插入到一个ul块中。

现在,让我们考虑一个使用Vue的例子:

其中最好的部分可能是使用v-for模板。注意Vue是如何做到与布局无关的(至少与JavaScript无关)。数据是从该API中获取的。它被分配了一个变量。布局处理如何显示它。我一直讨厌在我的JavaScript中使用HTML,但是jQuery提供了解决方案,把它嵌入到Vue中看起来就很自然很合适。

#一个完整的(在某种程度上有点琐碎)例子

为了更好地理解它,让我们考虑一个更真实的例子。我们的客户要求我们为一个产品API构建一个支持Ajax的前端搜索接口。功能列表包括:

  • 支持按名称和产品类别进行过滤
  • 我们必须提供搜索项或类别的表单验证
  • 当API被点击时,向用户显示一条消息并禁用提交按钮
  • 完成后,对未显示产品进行报告或列出匹配项

我们从jQuery版本开始。首先, HTML部分如下:

有一个带有两个过滤器和两个div的表单。一个用做搜索或报告错误时的临时状态,另一个用于呈现结果。现在,检查代码。

代码首先为要处理的每个DOM项(表单字段、按钮和div)创建一组变量。代码的逻辑核心在按钮的点击处理程序中。我们进行验证,如果一切正常,就对该API执行一个POST请求。当请求返回时,我们要么呈现结果

,要么在没有匹配的情况下显示消息。

你可以使用下面的CodePen来运行这个演示的完整版本。

现在让我们考虑Vue版本。同样,我们先从布局开始:

从顶部看,其中的变化包括:

  • 用一个div包装布局,可以让Vue知道在哪里运行。
  • 对表单字段使用v-model,以便它能轻松处理数据。
  • 使用@click.prevent处理执行主搜索操作。
  • 使用 :disabled 在这个Vue应用程序中将按钮绑定到一个值,无论按钮是否禁用 (我们稍后将看到它的实际操作)。
  • 状态值与前面的示例稍有不同。jQuery有一个特定的方法来设置DOM项中的文本和HTML中的文本,而Vue在将HTML分配给要呈现的值时需要使用v-html。如果我们在HTML中直接编写{{status}},标签将被转义。
  • 最后,使用v-if条件性地呈现结果列表,同时使用v-for来处理迭代。

现在让我们看看代码。

值得调用的第一个块是data字段集。有些映射到表单字段,有些映射到结果、状态消息等等。searchProducts方法处理的内容与jQuery版本大致相同,但通常直接绑定到DOM的代码要少得多。例如,即使我们知道结果是以一个无序列表列出的,但代码本身并不关心这一点。它只是进行赋值,标记才处理呈现值。总的来说,与jQuery代码相比,JavaScript代码更关心逻辑,jQuery代码“感觉”是更好的分离了关注点。

和以前一样,这里有一个CodePen可以让你自己试试:

#jQuery将死! Vue万岁!

好吧,这有点过分了。正如我在开始时所说的,如果你喜欢使用jQuery并且它对你有用的话,那我觉得你完全没必要更改任何东西。

不过,我想说的是,对于习惯使用jQuery的人来说,Vue似乎是一个很好的“下一步”。Vue支持复杂的应用程序,并为搭建和构建项目提供了一个非常棒的命令行工具。但是对于更简单的任务来说,Vue是一个很棒的“现代jQuery”的替代品,它已经成为我开发的可选工具!

有关使用Vue替代jQuery的另一个观点,请查看Sarah Drasner的“使用Vue.js替换jQuery:无需构建步骤”,因为它包含了其他一些超级有用的例子。

英文原文:https://css-tricks.com/making-the-move-from-jquery-to-vue/

译者:浣熊君( ????? )

、深浅拷贝的区别有哪些?

要说 js 的深浅拷贝,就不得不提 js 的两大数据类型:基本数据类型和引用类型。基本数据类型的变量名和值都存储在栈中,对于引用类型的变量名存储在栈中,而值存储在堆中。由于存储方式不同,所以导致了他们复制的时候方式不同。

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精准拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另外一个对象。

深拷贝是将一个对象从内存中完整的拷贝一份出来,从内存堆中存放一个新的对象。这是两个对象,所以修改其中一个,另外一个不会受影响。

深浅拷贝主要针对的是引用类型,简单数据类型不受影响。

相关笔试题

var person={
 name:"前端人",
 hobby:['学习','敲代码','潜水']
}
function copy(source){
 var newObj=new Object()
 for(var i in source){
  if(source.hasOwnProperty(i)){
   newObj[i]=source[i]
   }
  }
 return newObj
}
var p1=copy(person);
p1.name="Web Person"
console.log(person.name)
console.log(p1.name)
p1.hobby=["内卷"]
console.info(person.hobby)
console.info(p1.hobby)
/*运行结果:
前端人
 Web Person
["学习", "敲代码", "潜水"]
["内卷"]
*/

2、js 数据类型有哪些?

js 数据类型一共有 8 种,分为两大类:基本类型和引用类型。

它们的数据类型分别为:

基本类型:string、number、boolean、null、undefined、symbol、bigint

引用类型:object

相关面试题

// 注意:其他类型与数值进行相加时,其他类型的转为 number 类型
console.log( true+1 ) // 2
console.log( undefined +1 ) // NaN

console.log( null ) //object
console.log( undefined ) // undefined

3、延迟加载 js 的方式有哪些?有什么区别呢?

共有 6 种方式,分别为:

  • async
  • defer
  • js 最后加载
  • 利用 setTimeout
  • 动态创建 DOM 的方式
  • 使用 jQuery 的 getScript 方法

它们的区别介绍:

1、async:为 <script>标签定义了 async 属性。async 和 html 解析是同步的,不是顺次执行 js 脚本,谁先加载完成先执行谁。

<script  async type="text/javascript" src="demo1.js" ></script>
<script  async type="text/javascript" src="demo2.js" ></script>

2、defer 会等到 html 解析完成之后再执行 js 代码,如果有多个脚本时,会按照顺序依次执行脚本。

<script  defer type="text/javascript" src="demo1.js" ></script>

3、js 最后加载

把 js 外部引入的文件放置在页面的底部,让 js 最后加载,从而加快页面加载速度。

4、利用 setTimeout

5、动态创建 DOM 的方式

var element=document.createElement("script");  
element.src="box.js";  
document.body.appendChild(element);

这种方式通过操作动态加载 js 文件,不触发的时候不加载,减少页面文件大小,加快加载速度。

6、使用 jQuery 的 getScript 方法

$.getScript( "box.js",function(){//回调函数,成功获取文件后执行的函数  
      console.log("脚本加载完成")  
});

相关面试题:

<!doctype html>
<html>
 <head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script type="text/javascript" src="box.js"></script>
 </head>
 <body>
  <div id="box"></div>
 </body>
</html>

//box.js 代码如下
console.log( document.getElementById('box') )  // null

box.js 想正常获取元素 box ,并进行一系列操作应该如何延迟加载 js 文件呢?

4、你对作用域的认识有多少?

作用域通俗地讲,就是指一个变量的作用范围。分为全局作用域和函数作用域。

全局作用域

  • 页面打开时被创建,页面关闭时被销毁。
  • 编写在 script 标签下的变量和函数,作用域为全局,页面的任意位置都可以访问
  • 有全局对象 window ,代表浏览器窗口,全局作用下的变量和函数作为 window 的属性和方法

函数作用域(局部)

  • 函数是被调用时创建的,执行完毕之后销毁。
  • 函数每调用一次,变量和函数就会重新创建一次,它们之间是相互独立的
  • 在函数作用域内可以访问到全局变量或函数,但是在函数外无法访问函数作用域内的变量
  • 函数作用域内访问变量,会在自身作用域内寻找,若没有则会向上一级作用域内查找,一直到全局作用域。

函数在被调用的时候会先进行预编译:

全局作用域预编译:

  • 创建上下文 GO 对象。
  • 找变量声明,将变量名作为 GO 对象的属性名,值为 undefined
  • 找函数式声明,将值赋予函数体

函数作用域预编译:

  • 创建上下文 AO 对象
  • 将形参和实参作为 AO 对象的属性,赋值为 undefined
  • 实参和形参相统一
  • 在函数体内找函数声明,将值赋予函数体。

相关面试题:

<script type="text/javascript">
 function fn(a,c){
  console.log(a)
  var a=12
  console.log(a)
  console.log(c)
  function a(){ }
  if(false){
   var d=34
  }
  console.log(d)
  console.log(b)
  var b=function(){}
  console.log(b)
  function c(){}
  console.log(c)
 }
 fn(1,2)
</script>
// 运行结果:
/*
function a(){}
12
function c(){}
undefined
undefined
function (){}
function c(){}
*/

5、null 和 undefined 的区别。

null 和 undefined 两个都表示无的值。

作者设计 js 的时候,借鉴的 java 语言先设计的 null 。null 使用的时候会被隐式转化成 0,不容易发现错误。

console.log( number(null) ) //0

undefined 是为了填补 null 的坑。所以后来又新增了 undefined 。

console.log( number(undefined) ) //NaN

6、new 操作符具体做了什么?

  • 创建了一个空对象。
  • 将空对象的原型指向于构造函数的原型。
  • 将空对象作为构造函数的上下文。
  • 对构造函数有返回值的处理判断。

实现 new 操作符的方法:

function create( fn,...args ){
 var obj={}
 Object.setPrototypeOf( obj,fn.prototype )
 var resault=fn.apply(obj,args)
 return (resault instanceof Object) ? result : obj
}

7、为什么会有闭包?它解决了什么问题?

7.1、什么是闭包?

闭包就是函数嵌套函数,通过函数内的函数访问变量的规则,实现外部访问函数内的变量。

7.2、闭包的特点:

  • 函数嵌套函数。
  • 函数内部可以引用函数外部的参数和变量。
  • 参数和变量不会被垃圾回收机制回收。

实例3:闭包解决问题

var liArr=document.getElementsByTagName('li')
for(var i=0;i<liArr.length;i++){
 (function(i){
  liArr[i].onclick=function(){
   console.log('点击元素',liArr[i])
  }
 })(i) 
}

7.3、闭包优点:

  • 保护变量安全,实现封装,防止变量声明冲突和全局污染。
  • 在内存当中维持一个变量,可以做缓存。
  • 匿名函数自执行函数可以减少内存消耗。

防抖和节流就是闭包的经典应用。

7.4、闭包缺点:

  • 变量会驻留在内存中,造成内存损耗问题。解决办法:把闭包函数设置为 null 。
  • 内存泄漏

8、防抖和节流,你了解多少?

8.1、什么是防抖函数?

当持续触发事件,一定时间内没有再触发事件,事件处理函数才会执行一次,如果在设定的时间到来之前又触发了事件,就会重新计时。

防抖函数常见的实际应用:使用 echart 的时候,浏览器 resize 时,需要重新绘制图表大小,还有典型的输入框搜索应用。

8.2、节流函数是什么?

当持续触发事件的时候,保证一段时间内只调用一次事件处理函数,一段时间内,只允许做一件事情。

防抖和节流主要是用来限制触发频率较高的事件,再不影响效果的前提条件下,降低事件触发频率,减小浏览器或服务器的压力,提升用户体验效果。

9、数组去重有几种方法?

方法1: new set()

return Array.from(new Set(arr))
// 或
return [...new Set(arr)]

方法2:使用两次循环

for(var i=0,len=arr.length;i<len;i++){
 for(var j=i+1,len=arr.length;j<len;j++){
  if( arr[i]===arr[j] ){
   arr.splice(i,1)
   j--;
   len--
  }
 }
}
return arr

方法3:indexOf 实现

let arr1=[]
for(var i=0;i<arr.length;i++){
 if( arr1.indexOf(arr[i])===-1 ){
  arr1.push(arr[i])
 }
}
return arr1

方法4:includes 实现

let arr1=[]
for(var i=0;i<arr.length;i++){
 if( !arr1.includes(arr[i]) ){
  arr1.push(arr[i])
 }
}
return arr1

方法5:filter 实现

array.indexOf(item,start) start 表示开始检索的位置。

return arr.filter(( item, index )=>{
 return arr.indexOf( item, 0 )==index
})

10、call、bind 和 apply 的区别

三者都是改变函数执行的上下文,即改变 this 指向。

它们之间的区别为:

  • call 和 apply 会立即执行,bind 返回的是一个函数,需调用后执行。
  • 第二参数是传入要执行的方法中的参数,call 和 bind 是独立传递参数,apply 是以数组传递参数的

使用场景:
1、需要改变某个函数的this指向时
2、当参数较少时可以使用call,参数较多可以使用apply以数组的方式传递
3、当需要重复调用时,可以使用bind新定义一个方法

11、js 判断变量是不是数组,你能写出几种方法?

方法1:isArray

var arr=[1,2,3]
console.log(Array.isArray(arr))    

方法2:instanceof

var arr=[1,2,3]
console.log( arr instanceof Array )
console.log( arr instanceof Object )

该方法不够严谨。

方法3:prototype

console.log( Object.prototype.toString.call(arr).indexOf('Array')>-1 )

方法4:isPrototypeOf

console.log( Array.prototype.isPrototypeOf( arr ) )

方法5:constructor

console.log(arr.constructor.toString().indexOf('Array')>-1 )

12、slice 是干嘛的? splice 是否会改变原数组?

slice 是用来截取字符串的,返回一个新数组,但不会影响原数组。

使用语法:

arr.slice( start , end )

截取 arr 数组,从 start 开始到 end 结束,第二个参数是可选参数,没有时从 start 开始截取到结尾。

如果 start 参数是负数时,就会从 arr.lengtn + start 开始截取到结束。

var arr=['a','b','c','d','e']
console.log( arr.slice(-3) ) // ["c", "d", "e"]
console.log(arr)  //["a", "b", "c", "d", "e"]

splice 是一个更强大的方法,可以添加、删除、替换数组元素,返回的是被删除元素,它的操作会改变原数组。

使用语法:

splice( start, n, new )

从 start 开始,删除 n 个元素,然后把 new 添加到 start 元素之后。第三个参数为可选参数

  • n 为 0 且第三个参数不为空时,表示添加新元素到 start 之后。
  • n 不为 0 且第三个参数不为空时,表示把 start 之后的 n 个元素替换成 new 。
  • n 不为 0 且第三个参数为空时,表示删除 start 后的 n 个元素。
var arr=['a','b','c','d','e']
var ar=arr.splice( 1, 1 ,'f','g')
console.log('ar',ar)    // ["b"]
console.log('arr',arr) //  ["a", "f", "g", "c", "d", "e"]

13、==和===有什么不同?

==比较的是值,===除了比较值,还比较类型。

console.log( [1,2]=='1,2'  )       // true
console.log( [1,2]==='1,2'  )  //false

valueOf 方法返回 Math 对象的原始值,通常由 javascript 在后台自动调用,并不显示的出现在代码中。

console.log([1,2].valueOf()) //[1,2]
console.log('1,2'.valueOf()) //[1,2]
// 所以
console.log( [1,2]=='1,2'  )  // true

不管是字符串和数字比较,还是布尔值和数字比较,都会使用 valueOf 隐式转换。

总结:==需要使用 valueOf() 进行隐式转换,所以性能差。===会避开一些不必要的麻烦。

14、this 的指向

大厂笔试题:

var name='window name'
var p1={
 name:'p1 name',
 showName:function(){
  console.info(this.name)
 }
}
var fn=p1.showName
fn()
p1.showName()
var p2={
 name:'p2 name',
 showName:function(fun){
  fun()
 }
}
p2.showName(p1.showName)
p2.showName=p1.showName
p2.showName()
/*
运行结果:
window name
 p1 name
 window name
 p2 name
*/

这是一道关于 this 指向的面试题,接下来我们就说说 this 是如何指向的?

this 对象是运行时基于函数的执行环境绑定的:

  • 在全局函数中,this 等于 window 。
  • 函数上下文调用,严格模式下 this 为 undefined ,非严格模式下,this 指向 window 。
  • 当函数被作为某个对象的方法被调用时,this 等于那个对象。如果使用 call apply 改变当前 this 时,将会指向为传递过来的那个 this 。
  • 匿名函数的执行环境具有全局性,因此 this 指向 window。
  • 构造函数内的 this 指向创建的实例对象。
  • dom 事件处理函数,this 指向触发该事件的元素。
  • setTimeout 和 setInterval 中的 this 指向全局变量 window

15、js 中的继承有哪些方式呢?

第 1 种:原型链继承

function Parent(){
 this.name="前端人"
}
Parent.prototype.showName=function(){
 console.log(this.name)
}
function Child(){}
 //原型链继承   
Child.prototype=new Parent()
var p=new Child()
console.dir(p.name) //前端人

特点:

  • 实例的是子类的实例,也是父类的实例。
  • 父类新增原型方法和属性,子类都能访问到。
  • 简单,方便实现

第 2 种:借用构造函数

function Animal (name) {
 this.name=name || 'Animal';
 this.sleep=function(){
  console.log(this.name + '正在睡觉!');
 }
}
Animal.prototype.eat=function(food) {
 console.log(this.name + '正在吃:' + food);
};
function Cat(name){
 Animal.call(this);
 this.name=name || 'Tom';
}
// Test Code
var cat=new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:

  • 创建子类时,可以向父类传递参数。
  • 可以实现多继承,call 多个父类对象。
  • 解决方法1中,子类实例共享父类引用属性的问题。

还有组合式继承、ES6 的继承 和 寄生组合继承等等。每种继承方式都有各自的特点和缺点。

16、严格模式与非严格模式的区别,你了解多少?

JavaScript 语言是一门弱类型语言,存在许多类型错误,因此 ES6 引入了严格模式概念。

如果不加 ‘use strict’ 常规模式下就是属于非严格模式。

严格模式

在 js 文件顶部添加 ‘use strict’ 就属于严格模式,严格模式也可以指定在函数内部。

<script>
 'use strict'  
 //或者函数内部
 (function(){
  'use strict'
 })()
</script>

严格模式,是为 js 定义来了一种不同的解析与执行模型,在严格模式下,ECMAScipt 3 中一些不解和不确定的行为将得到处理,而且会对不安全的操作会抛出异常。‘use strict’ 会告诉浏览器引擎可以切换到严格模式执行。

严格模式与非严格模式区别

严格模式

非严格模式

变量必须声明才能赋值

变量不进行声明,可直接赋值

不能使用 delete 字符删除变量或对象

可以使用 delete 删除

函数参数变量名不允许重复

变量名重复,获取最后最后那个值

普通函数内的 this 为 undefined

普通函数内的 this 为 window

不允许使用八进制

允许任意进制

eval 和 arguments 当做关键字,不能被赋值和用作变量名

可以使用 eval 、arguments 作为变量名

call、apply 传入 null undefined 保持原样不被转为window

默认转为 window 对象

限制对调用栈的检测能力,访问 arguments.callee 会抛出异常

arguments.callee 运行正常

17、隐式转化相关面试题

console.log( '2'>10 ) //false
console.log( '2'>'10' ) //true
console.log( 'abc'>'b' ) //false
console.log( 'abc'>'aab' ) //true
console.log( undefined==null ) //true
console.log( NaN==NaN )//false
console.log( []==0 ) //true
console.log( ![]==0 ) //true
console.log( []==[] ) //false
console.log( {}=={} ) //false
console.log( {}==!{} ) //false

18、事件循环机制相关面试题。

阿里面试题1:

<script type="text/javascript">
 var p=new Promise(resolve=>{
  console.log(4)
  resolve(5)
 })
 function f1(){
  console.log(1)
 }
 function f2(){
  setTimeout(()=>{
   console.log(2)
  },0)
  f1()
  console.log(3)
  p.then(res=>{
   console.log(res)
  })
 }
 f2()
</script>
// 运行结果 4 1 3 5 2
// 如果已经了解事件运行机制,就可以跳过该问题了

事件循环机制,event-loop 。包含三部分:调用栈、消息队列、微任务队列。

事件循环开始的时候,会从全局一行一行的执行代码,遇到函数调用的时候,就会压入调用栈中,当函数执行完成之后,弹出调用栈。

// 如:代码会一行一行执行,函数全部调用完成之后清空调用栈
function f1(){
 console.log(1)
}
function f2(){
 f1()
 console.log(2)
}
f2()
// 执行结果 1 2

如果遇到 fetch、setInterval、setTimeout 异步操作时,函数调用压入调用栈时,异步执行内容会被加入消息队列中,消息队列中的内容会等到调用栈清空之后才会执行。

// 如:
function f1(){
 console.log(1)
}
function f2(){
 setTimeout(()=>{
  console.log(2)
 },0)
 f1()
 console.log(3)
}
f2()
// 执行结果 :1 3 2

遇到 promise、async、await 异步操作时,执行内容会被加入微任务队列中,会在调用栈清空之后立即执行。

调用栈加入的微任务队列会立即执行。

如
let p=new Promise(resolve=>{
 console.log('立即执行')
 resolve(1) //在 then 调用中执行
})

微任务队列中内容优先执行,所以比消息队列中的内容执行得早。

了解这些知识后,再试一下最前面的那道面试题,应该就没什么问题了。

20、前端领域内,你比较擅长什么?

这个问题就留给读到最后,能够坚持学习的人,问问我们自己有什么是我们擅长的?在哪块领域是我们占据竞争优势的?

、jQuery概述

什么是jquery

jQuery是JavaScript的一个库,jQuery 极大地简化了 JavaScript 编程。我们在做网站或web应用的过程中,需要用JavaScript为我们完成一些用户与页面的交互效果,jQuery库可以让我们用更少的JavaScript代码完成更多的功能。

jQuery的优势与劣势

几年前的web开发,浏览器兼容是一个非常重要的问题,前端工程师为了让自己的程序在多个浏览器中正确运行,常常需要编写更多的代码来解决浏览器兼容问题,jQuery出现之后,它为前端工程师们解决了大量的JavaScript兼容问题,并且封装了大量的DOM接口和动画效果,让我们可以用更少的代码实现更多的功能并且保持良好的兼容性,jQuery因此迅速成为了世界上最流行的JavaScript库。

jQuery的流行还有一个原因就是因为它简单、易上手。很多人在不熟悉JavaScript的情况下,仍然可以使用jQuery完成各种页面效果。

随着时间的推移,浏览器的兼容问题越来越少,css3也解决了大量的页面动画效果,jQuery的优势渐渐没有那么突出了,但是它仍然是当前被使用了最多的JavaScript库,是前端工程师必须掌握的技能。

二、jQuery代码的编写

引入jQuery

要使用jQuery,首先我们需要下载和引入jQuery,我们可以到jquery官网下载jQuery文件,然后在html页面中添加script标签引入jQuery。

我们将jQuery放在一个名为script的目录中,然后用下面的代码引入jQuery。

1 <body>
2     <script src="script/jquery.js"></script>
3     <script>
4         //这里可以编写jQuery代码
5     </script>
6 </body>

引入jQuery的注意事项

  • 如果要编写jQuery的代码,一定要用两个script标签,一个用来引入jQuery,一个用来编写代码,且不可将jQuery的代码写在第一个script标签当中,这样编写的代码将没有任何作用。
  • 一定要在第一个script引入jQuery,上面的script标签编写代码,下面的script引入jQuery,程序不能正常运行。

三、jQuery选择器

刚刚学习jQuery的时候,可以与css做比较:css使用【选择器】查找元素,使用【属性名:属性值】改变元素样式。jQuery与之类似,可以使用【jQuery选择器】查找元素,然后使用【jQuery方法】操作元素。这里的操作不只可以操作元素的样式,还可以添加和删除元素,或者获取元素的属性和文本等等功能。

改变元素样式

jQuery选择器和css选择器很类似(其实可以理解为jQuery选择器扩展了css选择器,在$()中添加css选择器就能找到我们想要的元素),下面我们使用jQuery选择器和css方法来找到并且改变一个元素的样式。

1 <body>
2     <h1>hello jquery</h1>
3     <script src="script/jquery.js"></script>
4     <script>
5         $("h1").css("color","red");  //将选择器$("h1")找到的元素的样式color设置成红色
6     </script>
7 </body>
  • 在上面的例子中$(“h1”)是选择器,我们可以在双引号内插入各种css选择器,jQuery就会找到响应的元素。
  • css称作jQuery方法,我们可以用jQuery方法来操作元素,这里面的css方法可以设置元素的样式,后续我们还会讲解其他的方法。
  • 使用方法的时候需要在方法名的后面添加一个括号,括号内可以添加参数,用来进一步描述方法的作用。

获取元素文本

上个例子中我们使用了一个jQuery的元素选择器和css方法实现了改变元素样式,接下来我们使用类选择器和text方法获取元素文本。

1 <body>
2     <h1 class="title">hello jquery</h1>
3     <script src="script/jquery.js"></script>
4     <script>
5         var txt=$(".title").text();
6         console.log(txt);
7     </script>
8 </body>
  • 在上面的代码中使用$(".title")选择器找到class值为title的元素,让后通过text方法获取元素的文本。
  • var可以声明一个变量,“=”可以将获取的文本赋值给txt变量。
  • 使用console.log()可以在控制台输出文本的内容。

设置元素属性

我们还可以通过jQuery的attr方法来设置元素的属性,下面的例子我们使用attr方法将img标签的src属性指定成一张图片的路径。

1 <body>
2     <img class="pic" src="">
3     <script src="script/jquery.js"></script>
4     <script>
5         $(".pic").attr("src","images/0.jpg");
6     </script>
7 </body>
  • 在上面的代码中,html标签img并没有设置src属性,我们通过jQuery的attr方法设置了img标签src属性。
  • attr方法可以设置两个参数,第一个参数是要设置的属性名,第二个参数是属性值。

添加和删除class

在说添加和删除class之前,我们先来看一个略复杂的选择器,代码如下所示。

 1 <body>
 2     <ul class="nav">
 3         <li>香蕉</li>
 4         <li>苹果</li>
 5         <li>鸭梨</li>
 6     </ul>
 7     <script src="script/jquery.js"></script>
 8     <script>
 9         $(".nav li:eq(1)").css("background-color","red");
10     </script>
11 </body>

上面的选择器$(".nav li:eq(1)")我们可以将其拆分来理解

  • ".nav"可以找到class名为nav的元素
  • ".nav li"可以找到class名为nav中的所有li元素
  • ".nav li:eq(1)"可以找到class名为nav中,li标签的第二个元素,这里需要注意的是元素的索引(编号)从0开始,所以1代表第二个元素。

在上面的代码中,我们将第二个li元素背景设置成了红色。

我们经常会使用jQuery操作元素的样式,上面我们已经学习了css方法,但是在实际开发中,css方法并不常用,我们更多的是使用addClass和removeClass方法来操作元素的样式,示例代码如下所示。


  • 通过addClass方法,我们可以给元素添加一个class名active,这样style标签中定义的样式就会作用于这个元素。
  • 同理,removeClass方法可以删除元素的class名,我们会在后续的课程中讲解。

课后练习

  1. 图卡片切换
  2. 选项卡
  3. 模态框

【融职教育】在工作中学习,在学习中工作