整合营销服务商

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

免费咨询热线:

JavaScript 侦测手机浏览器的五种方法

JavaScript 侦测手机浏览器的五种方法

时候,前端网页需要知道,用户使用的是手机浏览器还是桌面浏览器。

本文根据 StackOverflow,整理了 JavaScript 侦测手机浏览器的五种方法。

一、navigator.userAgent

最简单的方法就是分析浏览器的 user agent 字符串,它包含了设备信息。

JS 通过navigator.userAgent属性拿到这个字符串,只要里面包含mobi、android、iphone等关键字,就可以认定是移动设备。

if (/Mobi|Android|iPhone/i.test(navigator.userAgent)) { // 当前设备是移动设备 } // 另一种写法 if ( navigator.userAgent.match(/Mobi/i) || navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/iPhone/i) ) { // 当前设备是移动设备 }

这种方法的优点是简单方便,缺点是不可靠,因为用户可以修改这个字符串,让手机浏览器伪装成桌面浏览器。

Chromium 系的浏览器,还有一个navigator.userAgentData属性,也是类似的作用。不同之处是它将 user agent 字符串解析为一个对象,该对象的mobile属性,返回一个布尔值,表示用户是否使用移动设备。

const isMobile = navigator.userAgentData.mobile;

注意,苹果的 Safari 浏览器和 Firefox 浏览器都不支持这个属性,具体情况可以查看 Caniuse 网站。

此外,还有一个已经废除的navigator.platform属性,所有浏览器都支持,所以也可以用。它返回一个字符串,表示用户的操作系统。

if (/Android|iPhone|iPad|iPod/i.test(navigator.platform)) { // 当前设备是移动设备 }

二、window.screen,window.innerWidth

另一种方法是通过屏幕宽度,判断是否为手机。

window.screen对象返回用户设备的屏幕信息,该对象的width属性是屏幕宽度(单位为像素)。

if (window.screen.width < 500) { // 当前设备是移动设备 }

上面示例中,如果屏幕宽度window.screen.width小于500像素,就认为是手机。

这个方法的缺点在于,如果手机横屏使用,就识别不了。

另一个属性window.innerWidth返回浏览器窗口里面的网页可见部分的宽度,比较适合指定网页在不同宽度下的样式。

const getBrowserWidth = function() { if (window.innerWidth < 768) { return "xs"; } else if (window.innerWidth < 991) { return "sm"; } else if (window.innerWidth < 1199) { return "md"; } else { return "lg"; } };

三、window.orientation

第三种方法是侦测屏幕方向,手机屏幕可以随时改变方向(横屏或竖屏),桌面设备做不到。

window.orientation属性用于获取屏幕的当前方向,只有移动设备才有这个属性,桌面设备会返回undefined。

if (typeof window.orientation !== 'undefined') { // 当前设备是移动设备 }

注意,iPhone 的 Safari 浏览器不支持该属性。

四、touch 事件

第四种方法是,手机浏览器上的 DOM 元素可以通过ontouchstart属性,为touch事件指定监听函数。桌面设备没有这个属性。

function isMobile() { return ('ontouchstart' in document.documentElement); } // 另一种写法 function isMobile() { try { document.createEvent("TouchEvent"); return true; } catch(e) { return false; } }

五、window.matchMedia()

最后一种方法是结合 CSS 来判断。

CSS 通过 media query(媒介查询)为网页指定响应式样式。如果某个针对手机的 media query 语句生效了,就可以认为当前设备是移动设备。

window.matchMedia()方法接受一个 CSS 的 media query 语句作为参数,判断这个语句是否生效。

let isMobile = window.matchMedia("only screen and (max-width: 760px)").matches;

上面示例中,window.matchMedia()的参数是一个 CSS 查询语句,表示只对屏幕宽度不超过 700 像素的设备生效。它返回一个对象,该对象的matches属性是一个布尔值。如果是true,就表示查询生效,当前设备是手机。

除了通过屏幕宽度判断,还可以通过指针的精确性判断。

let isMobile = window.matchMedia("(pointer:coarse)").matches;

上面示例中,CSS 语句pointer:coarse表示当前设备的指针是不精确的。由于手机不支持鼠标,只支持触摸,所以符合这个条件。

有些设备支持多种指针,比如同时支持鼠标和触摸。pointer:coarse只用来判断主指针,此外还有一个any-pointer命令判断所有指针。

let isMobile = window.matchMedia("(any-pointer:coarse)").matches;

上面示例中,any-pointer:coarse表示所有指针里面,只要有一个指针是不精确的,就符合查询条件。

六、工具包

除了上面这些方法,也可以使用别人写好的工具包。这里推荐 react-device-detect,它支持多种粒度的设备侦测。

import {isMobile} from 'react-device-detect'; if (isMobile) { // 当前设备是移动设备 }

(完)

体上程序代码的执行是从上到下顺序执行,在某些具体阶段可以会产生分支和循环重复的情形,形成了各种不同的程序语句,构成了不同的程序结构。主要有顺序结构,分支结构和循环结构。以前的代码主要是按程序的顺序执行,以后的代码会是这三者的不同组合。

分支语句

1 语句

如果说表达式(例如:num1+num2)是短语。

语句就是完整的句子。(例如:sum=num1+num2;)

表达式计算出来的是一个值,语句来执行使某事情发生。

例如:给一个学生的年龄,输出这个学生是否成年了。

如果 年龄大于等于18岁 输出学生成年了。

这里的成年了是否会输出是有 年龄是否满足决定的。

JS在默认的就是从上到下一条条的执行代码语句。

为使某事发生可能会改变语句的默认执行顺序。

2 if语句

格式:if(条件){

条件为真实执行的语句

}

JS解释器会根据条件的结果决定是执行还是跳过语句。

读法:如果 条件为真,执行里面的代码。为假掉过里面的代码。

//实践:给一个学生的年龄,输出这个学生是否成年了。
//var age1=parseInt(prompt("请输入一个学生的年龄:"));
 var age1=23;
 if(age1>=18){
    console.log("这个学生成年了。");
 }
 if(age1<18){
    console.log("这个学生没有成年。");
 }

分析一下,这里的判断是一个事情的两个方面?

3 if-else语句

格式:

if(条件){条件为真,执行的语句}

else{条件为假(不为真),执行的语句。}

读法:如果 条件为真 执行为真的语句,否则执行为假的语句

//实践:给一个学生的年龄,输出这个学生是否成年了。
// var age2=parseInt(prompt("请输入一个学生的年龄:"));
var age2=16;
if(age2>=18){
console.log("这个学生成年了。");
}
else{
console.log("这个学生没有成年。");
}

如果判断学生的成绩等级,会有很多的判断。

4 if-else if语句

格式:

if(条件1){为真,执行的语句}

else if(条件2){为真,执行的语句}

......

else if(条件n){为真,执行的语句}

else{ 条件n为假,执行的语句 }

//实践:根据学生的成绩,判定学生的等级
//var score1=parseFloat(prompt("请输入一个学生的成绩:"));
var score1=95;
if(score1>=90){console.log("A");}
else if(score1>=80){console.log("B");}
else if(score1>=70){console.log("C");}
else if(score1>=60){console.log("D");}
else{console.log("E");}

5 switch语句

它是if语句的另外一种形式,解决根据表达式的不同而执行不同语句的问题。

格式:

switch(表达式){

case 值1:语句块;break;

case 值2:语句块;break;

......

case 值n:语句块;break;

default:语句块n+1;break;

}

执行:

计算一次 switch 表达式

每个表达式的值与每个 case 的值进行对比

如果存在匹配,则执行关联代码

default:关键词规定不存在 case 匹配时所运行的代码。

break:如果 JavaScript 遇到 break 关键词,它会跳出 switch 代码块。

//实践:重写根据学生的成绩,判定学生的等级
//var score2=parseInt(prompt("请输入一个学生的成绩:"));
var score2=90;
score2=parseInt(score2/10);//parseInt()取得整数部分。
switch(score2){
case 10:console.log("A");break;
case 9:console.log("A");break;
case 8:console.log("B");break;
case 7:console.log("C");break;
case 6:console.log("D");break;
default:console.log("E");break;}

6 三元运算符

可以当成一种简单的if-else语句。

名字表示的三元运算符需要三个操作数。

语法是:

条件 ? 结果1 : 结果2;

这里你把条件写在问号上(?)的前面后面跟着用冒号(:)分隔的结果1和结果2。

满足条件时结果1否则结果2。





这两天的GitHub Trending repositories被一个名叫 javascript-questions的项目霸榜了,项目中记录了一些JavaScript题目。



我大概从头到尾看了一遍,都是一些基础的题目,我大概花了半个小时(有些题很简单,可以一扫而过)把这些题做完了,虽然题目很简单,但是每道题都对应一个知识点,如果这个知识点你没有接触过,那肯定会做错,如果你接触过这些知识点,那么这些题对你来说就很容易。

建议大家也花半个小时来做一做,以便查漏补缺。

为方便大家能够更快的做题,而不把时间浪费在翻译上,我又花了几个小时把它们翻译成了中文,当然已经获得了作者授权。


文中有些点作者解释的不太完整,为了更好的理解,我在文中添加了一些个人解释。

仓库地址:https://github.com/lydiahallie/javascript-questions

JavaScript 进阶问题列表

我在我的Instagram上发布了每日JavaScript选择题,我也会在这里发布!

从基础到高级:测试您对JavaScript的了解程度,刷新您的知识,或为您的编码面试做好准备! 我每周用新问题更新这个项目。

答案位于问题下方的折叠部分,只需单击它们即可展开。 祝你好运??


1. 下面代码的输出是什么?

function sayHi() {
  console.log(name);
  console.log(age);
  var name="Lydia";
  let age=21;
}

sayHi();
复制代码
  • A: Lydia 和 undefined
  • B: Lydia 和 ReferenceError
  • C: ReferenceError 和 21
  • D: undefined 和 ReferenceError

答案


答案: D

在函数中,我们首先使用var关键字声明了name变量。 这意味着变量在创建阶段会被提升(JavaScript会在创建变量创建阶段为其分配内存空间),默认值为undefined,直到我们实际执行到使用该变量的行。 我们还没有为name变量赋值,所以它仍然保持undefined的值。

使用let关键字(和const)声明的变量也会存在变量提升,但与var不同,初始化没有被提升。 在我们声明(初始化)它们之前,它们是不可访问的。 这被称为“暂时死区”。 当我们在声明变量之前尝试访问变量时,JavaScript会抛出一个ReferenceError。

译者注:

关于let的是否存在变量提升,我们可以用下面的例子来验证:

let name='ConardLi'
{
  console.log(name) // Uncaught ReferenceError: name is not defined
  let name='code秘密花园'
}
复制代码

let变量如果不存在变量提升,console.log(name)就会输出ConardLi,结果却抛出了ReferenceError,那么这很好的说明了,let也存在变量提升,但是它存在一个“暂时死区”,在变量未初始化或赋值前不允许访问。

变量的赋值可以分为三个阶段:

  • 创建变量,在内存中开辟空间
  • 初始化变量,将变量初始化为undefined
  • 真正赋值

关于let、var和function:

  • let 的「创建」过程被提升了,但是初始化没有提升。
  • var 的「创建」和「初始化」都被提升了。
  • function 的「创建」「初始化」和「赋值」都被提升了。



2. 下面代码的输出是什么?

for (var i=0; i < 3; i++) {
  setTimeout(()=> console.log(i), 1);
}

for (let i=0; i < 3; i++) {
  setTimeout(()=> console.log(i), 1);
}
复制代码
  • A: 0 1 2 and 0 1 2
  • B: 0 1 2 and 3 3 3
  • C: 3 3 3 and 0 1 2

答案


答案: C

由于JavaScript中的事件执行机制,setTimeout函数真正被执行时,循环已经走完。 由于第一个循环中的变量i是使用var关键字声明的,因此该值是全局的。 在循环期间,我们每次使用一元运算符++都会将i的值增加1。 因此在第一个例子中,当调用setTimeout函数时,i已经被赋值为3。

在第二个循环中,使用let关键字声明变量i:使用let(和const)关键字声明的变量是具有块作用域的(块是{}之间的任何东西)。 在每次迭代期间,i将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。



3. 下面代码的输出是什么?

const shape={
  radius: 10,
  diameter() {
    return this.radius * 2;
  },
  perimeter: ()=> 2 * Math.PI * this.radius
};

shape.diameter();
shape.perimeter();
复制代码
  • A: 20 and 62.83185307179586
  • B: 20 and NaN
  • C: 20 and 63
  • D: NaN and 63

答案


答案: B

请注意,diameter是普通函数,而perimeter是箭头函数。

对于箭头函数,this关键字指向是它所在上下文(定义时的位置)的环境,与普通函数不同! 这意味着当我们调用perimeter时,它不是指向shape对象,而是指其定义时的环境(window)。没有值radius属性,返回undefined。



4. 下面代码的输出是什么?

+true;
!"Lydia";
复制代码
  • A: 1 and false
  • B: false and NaN
  • C: false and false

答案


答案: A

一元加号会尝试将boolean类型转换为数字类型。 true被转换为1,false被转换为0。

字符串'Lydia'是一个真值。 我们实际上要问的是“这个真值是假的吗?”。 这会返回false。



5. 哪个选项是不正确的?

const bird={
  size: "small"
};

const mouse={
  name: "Mickey",
  small: true
};
复制代码
  • A: mouse.bird.size
  • B: mouse[bird.size]
  • C: mouse[bird["size"]]
  • D: All of them are valid

答案


答案: A

在JavaScript中,所有对象键都是字符串(除了Symbol)。尽管有时我们可能不会给定字符串类型,但它们总是被转换为字符串。

JavaScript解释语句。当我们使用方括号表示法时,它会看到第一个左括号[,然后继续,直到找到右括号]。只有在那个时候,它才会对这个语句求值。

mouse [bird.size]:首先它会对bird.size求值,得到small。 mouse [“small”]返回true。

但是,使用点表示法,这不会发生。 mouse没有名为bird的键,这意味着mouse.bird是undefined。 然后,我们使用点符号来询问size:mouse.bird.size。 由于mouse.bird是undefined,我们实际上是在询问undefined.size。 这是无效的,并将抛出Cannot read property "size" of undefined。




6. 下面代码的输出是什么?

let c={ greeting: "Hey!" };
let d;

d=c;
c.greeting="Hello";
console.log(d.greeting);
复制代码
  • A: Hello
  • B: undefined
  • C: ReferenceError
  • D: TypeError

答案


答案: A

在JavaScript中,当设置它们彼此相等时,所有对象都通过引用进行交互。

首先,变量c为对象保存一个值。 之后,我们将d指定为c与对象相同的引用。

更改一个对象时,可以更改所有对象。



7. 下面代码的输出是什么?

let a=3;
let b=new Number(3);
let c=3;

console.log(a==b);
console.log(a===b);
console.log(b===c);
复制代码
  • A: true false true
  • B: false false true
  • C: true false false
  • D: false true true

答案


答案: C

new Number()是一个内置的函数构造函数。 虽然它看起来像一个数字,但它并不是一个真正的数字:它有一堆额外的功能,是一个对象。

当我们使用==运算符时,它只检查它是否具有相同的值。 他们都有3的值,所以它返回true。

译者注:==会引发隐式类型转换,右侧的对象类型会自动拆箱为Number类型。

然而,当我们使用===操作符时,类型和值都需要相等,new Number()不是一个数字,是一个对象类型。两者都返回 false。



8. 下面代码的输出是什么?

class Chameleon {
  static colorChange(newColor) {
    this.newColor=newColor;
  }

  constructor({ newColor="green" }={}) {
    this.newColor=newColor;
  }
}

const freddie=new Chameleon({ newColor: "purple" });
freddie.colorChange("orange");
复制代码
  • A: orange
  • B: purple
  • C: green
  • D: TypeError

答案


答案: D

colorChange方法是静态的。 静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。 由于freddie是一个子级对象,函数不会传递,所以在freddie实例上不存在freddie方法:抛出TypeError。



9. 下面代码的输出是什么?

let greeting;
greetign={}; // Typo!
console.log(greetign);
复制代码
  • A: {}
  • B: ReferenceError: greetign is not defined
  • C: undefined

答案


答案: A

控制台会输出空对象,因为我们刚刚在全局对象上创建了一个空对象! 当我们错误地将greeting输入为greetign时,JS解释器实际上在浏览器中将其视为global.greetign={}(或window.greetign={})。

为了避免这种情况,我们可以使用“use strict”。 这可以确保在将变量赋值之前必须声明变量。



10. 当我们这样做时会发生什么?

function bark() {
  console.log("Woof!");
}

bark.animal="dog";
复制代码
  • A: Nothing, this is totally fine!
  • B: SyntaxError. You cannot add properties to a function this way.
  • C: undefined
  • D: ReferenceError

答案


答案: A

这在JavaScript中是可能的,因为函数也是对象!(原始类型之外的所有东西都是对象)

函数是一种特殊类型的对象。您自己编写的代码并不是实际的函数。 该函数是具有属性的对象,此属性是可调用的。



11. 下面代码的输出是什么?

function Person(firstName, lastName) {
  this.firstName=firstName;
  this.lastName=lastName;
}

const member=new Person("Lydia", "Hallie");
Person.getFullName=()=> this.firstName + this.lastName;

console.log(member.getFullName());
复制代码
  • A: TypeError
  • B: SyntaxError
  • C: Lydia Hallie
  • D: undefined undefined

答案


答案: A

您不能像使用常规对象那样向构造函数添加属性。 如果要一次向所有对象添加功能,则必须使用原型。 所以在这种情况下应该这样写:

Person.prototype.getFullName=function () {
  return `${this.firstName} ${this.lastName}`;
}
复制代码

这样会使member.getFullName()是可用的,为什么样做是对的? 假设我们将此方法添加到构造函数本身。 也许不是每个Person实例都需要这种方法。这会浪费大量内存空间,因为它们仍然具有该属性,这占用了每个实例的内存空间。 相反,如果我们只将它添加到原型中,我们只需将它放在内存中的一个位置,但它们都可以访问它!



12. 下面代码的输出是什么?

function Person(firstName, lastName) {
  this.firstName=firstName;
  this.lastName=lastName;
}

const lydia=new Person("Lydia", "Hallie");
const sarah=Person("Sarah", "Smith");

console.log(lydia);
console.log(sarah);
复制代码
  • A: Person {firstName: "Lydia", lastName: "Hallie"} and undefined
  • B: Person {firstName: "Lydia", lastName: "Hallie"} and Person {firstName: "Sarah", lastName: "Smith"}
  • C: Person {firstName: "Lydia", lastName: "Hallie"} and {}
  • D:Person {firstName: "Lydia", lastName: "Hallie"} and ReferenceError

答案


答案: A

对于sarah,我们没有使用new关键字。 使用new时,它指的是我们创建的新空对象。 但是,如果你不添加new它指的是全局对象!

我们指定了this.firstName等于'Sarah和this.lastName等于Smith。 我们实际做的是定义global.firstName='Sarah'和global.lastName='Smith。 sarah本身的返回值是undefined。



12. 事件传播的三个阶段是什么??

  • A: 目标 > 捕获 > 冒泡
  • B: 冒泡 > 目标 > 捕获
  • C: 目标 > 冒泡 > 捕获
  • D: 捕获 > 目标 > 冒泡

答案


答案: D

在捕获阶段,事件通过父元素向下传递到目标元素。 然后它到达目标元素,冒泡开始。



13. 所有对象都有原型.

  • A: 对
  • B: 错误

答案


答案: B

基础对象外,所有对象都有原型。 基础对象可以访问某些方法和属性,例如.toString。 这就是您可以使用内置JavaScript方法的原因! 所有这些方法都可以在原型上找到。 虽然JavaScript无法直接在您的对象上找到它,但它会沿着原型链向下寻找并在那里找到它,这使您可以访问它。

译者注:基础对象指原型链终点的对象。基础对象的原型是null。



14. 下面代码的输出是什么?

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

sum(1, "2");
复制代码
  • A: NaN
  • B: TypeError
  • C: "12"
  • D: 3

答案


答案: C

JavaScript是一种动态类型语言:我们没有指定某些变量的类型。 在您不知情的情况下,值可以自动转换为另一种类型,称为隐式类型转换。 强制从一种类型转换为另一种类型。

在此示例中,JavaScript将数字1转换为字符串,以使函数有意义并返回值。 在让数字类型(1)和字符串类型('2')相加时,该数字被视为字符串。 我们可以连接像“Hello”+“World”这样的字符串,所以这里发生的是“1”+“2”返回“12”。



15. 下面代码的输出是什么?

let number=0;
console.log(number++);
console.log(++number);
console.log(number);
复制代码
  • A: 1 1 2
  • B: 1 2 2
  • C: 0 2 2
  • D: 0 1 2

答案


答案: C

后缀一元运算符++:

  1. 返回值(返回0)
  2. 增加值(数字现在是1)

前缀一元运算符++:

  1. 增加值(数字现在是2)
  2. 返回值(返回2)

所以返回0 2 2。



16. 下面代码的输出是什么?

function getPersonInfo(one, two, three) {
  console.log(one);
  console.log(two);
  console.log(three);
}

const person="Lydia";
const age=21;

getPersonInfo`${person} is ${age} years old`;
复制代码
  • A: Lydia 21 ["", "is", "years old"]
  • B: ["", "is", "years old"] Lydia 21
  • C: Lydia ["", "is", "years old"] 21

答案


答案: B

如果使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。 其余参数获取传递到模板字符串中的表达式的值!



17. 下面代码的输出是什么?

function checkAge(data) {
  if (data==={ age: 18 }) {
    console.log("You are an adult!");
  } else if (data=={ age: 18 }) {
    console.log("You are still an adult.");
  } else {
    console.log(`Hmm.. You don't have an age I guess`);
  }
}

checkAge({ age: 18 });
复制代码
  • A: You are an adult!
  • B: You are still an adult.
  • C: Hmm.. You don't have an age I guess

答案


答案: C

在比较相等性,原始类型通过它们的值进行比较,而对象通过它们的引用进行比较。JavaScript检查对象是否具有对内存中相同位置的引用。

我们作为参数传递的对象和我们用于检查相等性的对象在内存中位于不同位置,所以它们的引用是不同的。

这就是为什么{ age: 18 }==={ age: 18 }和 { age: 18 }=={ age: 18 } 返回 false的原因。



18. 下面代码的输出是什么?

function getAge(...args) {
  console.log(typeof args);
}

getAge(21);
复制代码
  • A: "number"
  • B: "array"
  • C: "object"
  • D: "NaN"

答案


答案: C

扩展运算符(... args)返回一个带参数的数组。 数组是一个对象,因此typeof args返回object。



20. 下面代码的输出是什么?

function getAge() {
  "use strict";
  age=21;
  console.log(age);
}

getAge();
复制代码
  • A: 21
  • B: undefined
  • C: ReferenceError
  • D: TypeError

答案


答案: C

使用“use strict”,可以确保不会意外地声明全局变量。 我们从未声明变量age,因为我们使用``use strict',它会引发一个ReferenceError。 如果我们不使用“use strict”,它就会起作用,因为属性age`会被添加到全局对象中。



21. 下面代码的输出是什么?

const sum=eval("10*10+5");
复制代码
  • A: 105
  • B: "105"
  • C: TypeError
  • D: "10*10+5"

答案


答案: A

eval会为字符串传递的代码求值。 如果它是一个表达式,就像在这种情况下一样,它会计算表达式。 表达式为10 * 10 + 5计算得到105。



22. cool_secret可以访问多长时间?

sessionStorage.setItem("cool_secret", 123);
复制代码
  • A:永远,数据不会丢失。
  • B:用户关闭选项卡时。
  • C:当用户关闭整个浏览器时,不仅是选项卡。
  • D:用户关闭计算机时。

答案


答案: B

关闭选项卡后,将删除存储在sessionStorage中的数据。

如果使用localStorage,数据将永远存在,除非例如调用localStorage.clear()。



23. 下面代码的输出是什么?

var num=8;
var num=10;

console.log(num);
复制代码
  • A: 8
  • B: 10
  • C: SyntaxError
  • D: ReferenceError

答案


答案: B

使用var关键字,您可以用相同的名称声明多个变量。然后变量将保存最新的值。

您不能使用let或const来实现这一点,因为它们是块作用域的。



24. 下面代码的输出是什么?

const obj={ 1: "a", 2: "b", 3: "c" };
const set=new Set([1, 2, 3, 4, 5]);

obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
复制代码
  • A: false true false true
  • B: false true true true
  • C: true true false true
  • D: true true true true

答案


答案: C

所有对象键(不包括Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。 这就是为什么obj.hasOwnProperty('1')也返回true。

上面的说法不适用于Set。 在我们的Set中没有“1”:set.has('1')返回false。 它有数字类型1,set.has(1)返回true。



25. 下面代码的输出是什么?

const obj={ a: "one", b: "two", a: "three" };
console.log(obj);
复制代码
  • A: { a: "one", b: "two" }
  • B: { b: "two", a: "three" }
  • C: { a: "three", b: "two" }
  • D: SyntaxError

答案


答案: C

如果对象有两个具有相同名称的键,则将替前面的键。它仍将处于第一个位置,但具有最后指定的值。



26. JavaScript全局执行上下文为你创建了两个东西:全局对象和this关键字.

  • A: 对
  • B: 错误
  • C: 视情况而定

答案


答案: A

基本执行上下文是全局执行上下文:它是代码中随处可访问的内容。



27. 下面代码的输出是什么?

for (let i=1; i < 5; i++) {
  if (i===3) continue;
  console.log(i);
}
复制代码
  • A: 1 2
  • B: 1 2 3
  • C: 1 2 4
  • D: 1 3 4

答案


答案: C

如果某个条件返回true,则continue语句跳过迭代。



28. 下面代码的输出是什么?

String.prototype.giveLydiaPizza=()=> {
  return "Just give Lydia pizza already!";
};

const name="Lydia";

name.giveLydiaPizza();
复制代码
  • A: "Just give Lydia pizza already!"
  • B: TypeError: not a function
  • C: SyntaxError
  • D: undefined

答案


答案: A

String是一个内置的构造函数,我们可以为它添加属性。 我刚给它的原型添加了一个方法。 原始类型的字符串自动转换为字符串对象,由字符串原型函数生成。 因此,所有字符串(字符串对象)都可以访问该方法!

译者注:

当使用基本类型的字符串调用giveLydiaPizza时,实际上发生了下面的过程:

  • 创建一个String的包装类型实例
  • 在实例上调用substring方法
  • 销毁实例



29. 下面代码的输出是什么?

const a={};
const b={ key: "b" };
const c={ key: "c" };

a[b]=123;
a[c]=456;

console.log(a[b]);
复制代码
  • A: 123
  • B: 456
  • C: undefined
  • D: ReferenceError

答案


答案: B

对象键自动转换为字符串。我们试图将一个对象设置为对象a的键,其值为123。

但是,当对象自动转换为字符串化时,它变成了[Object object]。 所以我们在这里说的是a["Object object"]=123。 然后,我们可以尝试再次做同样的事情。 c对象同样会发生隐式类型转换。那么,a["Object object"]=456。

然后,我们打印a[b],它实际上是a["Object object"]。 我们将其设置为456,因此返回456。



30. 下面代码的输出是什么?

const foo=()=> console.log("First");
const bar=()=> setTimeout(()=> console.log("Second"));
const baz=()=> console.log("Third");

bar();
foo();
baz();
复制代码
  • A: First Second Third
  • B: First Third Second
  • C: Second First Third
  • D: Second Third First

答案


答案: B

我们有一个setTimeout函数并首先调用它。 然而却最后打印了它。

这是因为在浏览器中,我们不只有运行时引擎,我们还有一个叫做WebAPI的东西。WebAPI为我们提供了setTimeout函数,例如DOM。

将callback推送到WebAPI后,setTimeout函数本身(但不是回调!)从堆栈中弹出。

现在,调用foo,并打印First。

foo从堆栈弹出,baz被调用,并打印Third。

WebAPI不能只是在准备就绪时将内容添加到堆栈中。 相反,它将回调函数推送到一个称为任务队列的东西。

这是事件循环开始工作的地方。 事件循环查看堆栈和任务队列。 如果堆栈为空,则会占用队列中的第一个内容并将其推送到堆栈中。

bar被调用,Second被打印,它从栈中弹出。



31. 单击按钮时event.target是什么?

<div onclick="console.log('first div')">
  <div onclick="console.log('second div')">
    <button onclick="console.log('button')">
      Click!
    </button>
  </div>
</div>
复制代码
  • A: div外部
  • B: div内部
  • C: button
  • D: 所有嵌套元素的数组.

答案


答案: C

导致事件的最深嵌套元素是事件的目标。 你可以通过event.stopPropagation停止冒泡



32. 单击下面的html片段打印的内容是什么?

<div onclick="console.log('div')">
  <p onclick="console.log('p')">
    Click here!
  </p>
</div>
复制代码
  • A: p div
  • B: div p
  • C: p
  • D: div

答案


答案: A

如果我们单击p,我们会看到两个日志:p和div。在事件传播期间,有三个阶段:捕获,目标和冒泡。 默认情况下,事件处理程序在冒泡阶段执行(除非您将useCapture设置为true)。 它从最深的嵌套元素向外延伸。



33. 下面代码的输出是什么?

const person={ name: "Lydia" };

function sayHi(age) {
  console.log(`${this.name} is ${age}`);
}

sayHi.call(person, 21);
sayHi.bind(person, 21);
复制代码
  • A: undefined is 21 Lydia is 21
  • B: function function
  • C: Lydia is 21 Lydia is 21
  • D: Lydia is 21 function

答案


答案: D

使用两者,我们可以传递我们想要this关键字引用的对象。 但是,.call方法会立即执行!

.bind方法会返回函数的拷贝值,但带有绑定的上下文! 它不会立即执行。



34. 下面代码的输出是什么?

function sayHi() {
  return (()=> 0)();
}

typeof sayHi();
复制代码
  • A: "object"
  • B: "number"
  • C: "function"
  • D: "undefined"

答案


答案: B

sayHi函数返回立即调用的函数(IIFE)的返回值。 该函数返回0,类型为数字。

仅供参考:只有7种内置类型:null,undefined,boolean,number,string,object和symbol。 function不是一个类型,因为函数是对象,它的类型是object。



35. 下面这些值哪些是假值?

0;
new Number(0);
("");
(" ");
new Boolean(false);
undefined;
复制代码
  • A: 0, '', undefined
  • B: 0, new Number(0), '', new Boolean(false), undefined
  • C: 0, '', new Boolean(false), undefined
  • D: 所有都是假值

答案


答案: A

JavaScript中只有6个假值:

  • undefined
  • null
  • NaN
  • 0
  • '' (empty string)
  • false

函数构造函数,如new Number和new Boolean都是真值。



36. 下面代码的输出是什么?

console.log(typeof typeof 1);
复制代码
  • A: "number"
  • B: "string"
  • C: "object"
  • D: "undefined"

答案


答案: B

typeof 1 返回 "number". typeof "number" 返回 "string"



37. 下面代码的输出是什么?

const numbers=[1, 2, 3];
numbers[10]=11;
console.log(numbers);
复制代码
  • A: [1, 2, 3, 7 x null, 11]
  • B: [1, 2, 3, 11]
  • C: [1, 2, 3, 7 x empty, 11]
  • D: SyntaxError

答案


答案: C

当你为数组中的元素设置一个超过数组长度的值时,JavaScript会创建一个名为“空插槽”的东西。 这些位置的值实际上是undefined,但你会看到类似的东西:

[1, 2, 3, 7 x empty, 11]

这取决于你运行它的位置(每个浏览器有可能不同)。



38. 下面代码的输出是什么?

(()=> {
  let x, y;
  try {
    throw new Error();
  } catch (x) {
    (x=1), (y=2);
    console.log(x);
  }
  console.log(x);
  console.log(y);
})();
复制代码
  • A: 1 undefined 2
  • B: undefined undefined undefined
  • C: 1 1 2
  • D: 1 undefined undefined

答案


答案: A

catch块接收参数x。当我们传递参数时,这与变量的x不同。这个变量x是属于catch作用域的。

之后,我们将这个块级作用域的变量设置为1,并设置变量y的值。 现在,我们打印块级作用域的变量x,它等于1。

在catch块之外,x仍然是undefined,而y是2。 当我们想在catch块之外的console.log(x)时,它返回undefined,而y返回2。



39. JavaScript中的所有内容都是...

  • A:原始或对象
  • B:函数或对象
  • C:技巧问题!只有对象
  • D:数字或对象

答案


答案: A

JavaScript只有原始类型和对象。

原始类型是boolean,null,undefined,bigint,number,string和symbol。



40. 下面代码的输出是什么?

[[0, 1], [2, 3]].reduce(
  (acc, cur)=> {
    return acc.concat(cur);
  },
  [1, 2]
);
复制代码
  • A: [0, 1, 2, 3, 1, 2]
  • B: [6, 1, 2]
  • C: [1, 2, 0, 1, 2, 3]
  • D: [1, 2, 6]

答案


答案: C

[1,2]是我们的初始值。 这是我们开始执行reduce函数的初始值,以及第一个acc的值。 在第一轮中,acc是[1,2],cur是[0,1]。 我们将它们连接起来,结果是[1,2,0,1]。

然后,acc的值为[1,2,0,1],cur的值为[2,3]。 我们将它们连接起来,得到[1,2,0,1,2,3]。



41. 下面代码的输出是什么?

!!null;
!!"";
!!1;
复制代码
  • A: false true false
  • B: false false true
  • C: false true true
  • D: true true false

答案


答案: B

null是假值。 !null返回true。 !true返回false。

""是假值。 !""返回true。 !true返回false。

1是真值。 !1返回false。 !false返回true。



42. setInterval方法的返回值什么?

setInterval(()=> console.log("Hi"), 1000);
复制代码
  • A:一个唯一的id
  • B:指定的毫秒数
  • C:传递的函数
  • D:undefined

答案


答案: A

它返回一个唯一的id。 此id可用于使用clearInterval()函数清除该定时器。



43. 下面代码的返回值是什么?

[..."Lydia"];
复制代码
  • A: ["L", "y", "d", "i", "a"]
  • B: ["Lydia"]
  • C: [[], "Lydia"]
  • D: [["L", "y", "d", "i", "a"]]

答案


答案: A

字符串是可迭代的。 扩展运算符将迭代的每个字符映射到一个元素。


小结

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。

想阅读更多优质文章、可关注我的github博客,你的star?、点赞和关注是我持续创作的动力!


github : https://github.com/ConardLi/ConardLi.github.io

原链接:https://juejin.im/post/5d0644976fb9a07ed064b0ca