zoukankan      html  css  js  c++  java
  • JavaScript 中 几 个需要掌握基础的问题

    1.如何从数组中移除一个特定的项

    思路:首先,使用indexOf查找要删除的数组元素的索引(index),然后使用splice方法删除该索引所对应的项。

    splice()是一个非纯函数,通过删除现有元素和/或添加新元素来更改数组的内容。

    const array = [2, 5, 9]
    
    const index = array.indexOf(5)
    if (index > -1) {
      array.splice(index, 1)
    }
    
    console.log(array)
    // [ 2, 9 ]

    splice的第二个参数是要删除的元素数量。注意,splice会在适当的位置修改数组,并返回一个包含已删除元素的新数组。

    接着,我们可以来完善一下。下面有两个函数,第一个函数仅删除一个匹配项(即从[2,5,9,1,5,8,5]中删除第一个匹配项5),而第二个函数则删除所有匹配项:

    // 仅删除第一个匹配项
    function removeItemOnce (arr, value) {
      let index = arr.indexOf(value)
      if (index > -1) {
        arr.splice(index, 1)
      }
      return arr
    }
    
    // 删除所有匹配项
    function removeItemAll (arr, value) {
      let i = 0
      while(i < arr.length) {
        if (arr[i] === value) {
          arr.splice(i, 1)
        } else {
          ++i
        }
      }
    }

    删除数组中索引i处的元素:

    array.splice(i, 1)

    如果你想从数组中删除值为number的每个元素,可以这样做:

    for (let i = array.length - 1; i>=0; i--) {
      if (array[i] === number) {
        array.splice(i, 1)
      }
    }

    如果你只想使索引i处的元素不再存在,但又不想更改其他元素的索引:

    delete array[i]

    2. 如何使用 jQuery 或纯 JS 将用户从一个页面重定向到另一个页面

    jQuery 不是必需的,window.location.replace(…)最适合模拟 HTTP 重定向。window.location.replace(...)优于使用window.location.href,因为replace()不会将原始页面保留在会话历史记录中,这意味着用户将不会陷入永无休止回退按钮。

    如果要模拟单击链接,可以使用location.href,如果要模拟HTTP重定向,请使用location.replace

    //模拟HTTP重定向
    window.location.replace("http://stackoverflow.com")
    
    // 模拟单击链接
    window.location.href = "http://stackoverflow.com"

    你还可以这样做:

    $(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

    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. 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
    
    ' 	
     ' == 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
    c === d           // false
    
    e == f            // true
    e === f           // true

    特殊情况是,当你将一个字符串字面量与一个字符串对象进行比较时,由于该对象的toStringvalueOf方法,该对象的值与相字面量的值一样。

    考虑将字符串字面量与由String构造函数创建的字符串对象进行比较:

    "abc" == new String("abc")    // true
    "abc" === new String("abc")   // false

    在这里,==操作符检查两个对象的值并返回true,但是===看到它们不是同一类型并返回false。哪一个是正确的?这取决于你想要比较的是什么。

    我们的建议是完全绕开该问题,只是不要使用String构造函数来创建字符串对象。

    使用==运算符(等于)

    true == 1; //true, 因为 true 被转换为1,然后进行比较
    "2" == 2;  //true, 因为 “2” 被转换成 2,然后进行比较

    使用===操作符

    true === 1; //false
    "2" === 2;  //false

    7.如何在另一个JavaScript文件中包含一个JavaScript文件?

    旧版本的JavaScript没有importincluderequire,因此针对这个问题开发了许多不同的方法。

    但是从2015年(ES6)开始,JavaScript已经有了ES6模块标准,可以在Node中导入模块。为了与旧版浏览器兼容,可以使用Webpack和Rollup之类的构建工具和/或Babel这样的编译工具。

    ES6 Module

    从v8.5开始,Node.js就支持ECMAScript (ES6)模块,带有--experimental-modules标志,而且至少Node.js v13.8.0没有这个标志。要启用ESM(相对于Node.js之前的commonjs风格的模块系统[CJS]),你可以在 package.json中使用“type”:“module”。或者为文件提供扩展名.mjs。(类似地,如果默认为ESM,则用 Node.js 以前的CJS模块编写的模块可以命名为.cjs。)

    使用package.json

    {
        "type": "module"
    }

    在 module.js: 中

    export function hello() {
      return "Hello";
    }

    main.js:

    import { hello } from './module.js';
    let val = hello();  // val is "Hello";

    使用.mjs,会有对应的module.mjs

    export function hello() {
      return "Hello";
    }

    main.mjs 中

    import { hello } from './module.mjs';
    let val = hello();  // val is "Hello";

    自Safari 10.1,Chrome 61,Firefox 60 和 Edge 16 开始,浏览器就已经支持直接加载ECMAScript模块(不需要像Webpack这样的工具)。无需使用Node.js的.mjs扩展名; 浏览器完全忽略模块/脚本上的文件扩展名。

    <script type="module">
      import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
      hello('world');
    </script>
    
    // hello.mjs -- or it could be simply `hello.js`
    export function hello(text) {
      const div = document.createElement('div');
      div.textContent = `Hello ${text}`;
      document.body.appendChild(div);
  • 相关阅读:
    WebService的学习
    什么是事物
    数组和链表的区别
    JDK6和JDK7中的substring()方法
    为什么存储密码字符数组比字符串更合适?
    java中Queue简介
    java中队列Queue的使用
    HashMap、Hashtable、TreeMap的区别
    笔试算法题(36):寻找一棵二叉树中最远节点的距离 & 根据二叉树的前序和后序遍历重建二叉树
    笔试算法题(35):最长递增子序列 & 判定一个字符串是否可由另一个字符串旋转得到
  • 原文地址:https://www.cnblogs.com/ZXH-null/p/13819492.html
Copyright © 2011-2022 走看看