分享30个你必须知道的JS基础知识

a75cd7507eeb9d4fee243f80cac2af33.jpeg

今天这篇文章整理了30个你必须知道的关于JavaScript的常识点,无论你是面试还是日常开发,都会给你很大的帮助。

1. ‘&&’运算符能做什么?

&& 运算符,也称为逻辑与,计算操作数并返回它遇到的第一个假表达式。 如果没有找到错误的表达式,它会返回最后一个真实的表达式。 它采用短路来防止不必要的工作。

console.log(false && 1 && []); // false
console.log(" " && true && 5); // 5

使用 if 语句。

const router: Router = Router();


router.get('/endpoint', (req: Request, res: Response) => {
   let conMobile: PoolConnection;
   try {
      //do some db operations
   } catch (e) {
   if (conMobile) {
    conMobile.release();
   }
  }
});

使用 && 运算符。

const router: Router = Router();


router.get('/endpoint', (req: Request, res: Response) => {
  let conMobile: PoolConnection;
  try {
     //do some db operations
  } catch (e) {
    conMobile && conMobile.release()
  }
});

2.||可以做什么呢?

|| 运算符,也称为逻辑或,计算操作数并返回它遇到的第一个真值表达式。 它还使用短路来防止不必要的工作。 在ES6引入默认函数参数之前,它被用来初始化函数中的默认参数值。

console.log(null || 1 || undefined); // 1


function logName(name) {
  var n = name || "Mark";
  console.log(n);
}


logName(); // "Mark"

3. undefined 和 null 有什么区别?

在了解 undefined 和 null 的区别之前,我们先来看看它们的相同点。

它们都属于 JavaScript 中的 7 种基本类型。

let primitiveTypes = ['string','number','null','undefined','boolean','symbol', 'bigint'];

它们被认为是假值,这意味着当使用 Boolean(value) 或 !!value 转换为布尔值时,它们的值为假。

console.log(!!null); // false
console.log(!!undefined); // false


console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false

现在,让我们看看它们的区别。

undefined 是未指定特定值的变量或未显式返回值的函数的默认值,例如 console.log(1),以及对象中不存在的属性,JS 引擎为其分配未定义的值。

let _thisIsUndefined;
const doNothing = () => {};
const someObj = {
  a : "ay",
  b : "bee",
  c : "si"
};


console.log(_thisIsUndefined); // undefined
console.log(doNothing()); // undefined
console.log(someObj["d"]); // undefined

null 是一个值,表示没有任何值。 nullis 显式分配给变量。 在此示例中,当 fs.readFile 方法未抛出错误时,我们将获得一个空值。

fs.readFile('path/to/file', (e,data) => {
   console.log(e); // Print null when no errors occur.
   if(e){
     console.log(e);
   }
   console.log(data);
 });

比较 null 和 undefined 时,使用 == 时得到 true,使用 === 时得到 false。

console.log(null == undefined); // true
 console.log(null === undefined); // false

4.什么是事件传播?

当一个事件发生在 DOM 元素上时,它不仅仅发生在那个特定的元素上。 在“冒泡阶段”期间,事件冒泡或传播到其父级、祖父母、曾祖父母或父级,直到到达窗口。 

另一方面,在“捕获阶段”期间,事件从窗口开始在元素上触发,并向下传播到事件目标或 event.target。

事件传播分为三个阶段:

捕获阶段:事件从窗口开始,然后向下传播到每个元素,直到到达目标元素。

目标阶段:事件已经到达目标元素。

冒泡阶段:事件从目标元素冒泡,然后上升到每个元素,直到到达窗口。

5.什么是事件冒泡?

当一个事件发生在 DOM 元素上时,它不仅仅发生在那个特定的元素上。 在冒泡阶段,事件冒泡,或者事件发生在它的父级、祖父母、曾祖父母等上,直到它到达窗口。

HTML:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>

JS:

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}


addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');


  addEvent(child, 'click', function (e) {
    console.log('child');
  });


  addEvent(parent, 'click', function (e) {
    console.log('parent');
  });


  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  });


  addEvent(document, 'click', function (e) {
    console.log('document');
  });


  addEvent('html', 'click', function (e) {
    console.log('html');
  })


  addEvent(window, 'click', function (e) {
    console.log('window');
  })


});

addEventListener 方法有第三个可选参数,称为 useCapture,默认为 false。 如果设置为true,事件将发生在捕获阶段而不是冒泡阶段。 如果点击子元素,它会在控制台上分别记录child、parent、grandparent、html、document、window。 这就是事件冒泡。

6.什么是事件捕捉?

当一个事件发生在 DOM 元素上时,它不仅仅发生在那个特定的元素上。 在捕获阶段,事件从窗口开始,一直向下传播到触发事件的元素。

HTML:

<div class="grandparent">
  <div class="parent">
    <div class="child">1</div>
  </div>
</div>

JS:

function addEvent(el, event, callback, isCapture = false) {
  if (!el || !event || !callback || typeof callback !== 'function') return;
  if (typeof el === 'string') {
    el = document.querySelector(el);
  };
  el.addEventListener(event, callback, isCapture);
}


addEvent(document, 'DOMContentLoaded', () => {
  const child = document.querySelector('.child');
  const parent = document.querySelector('.parent');
  const grandparent = document.querySelector('.grandparent');


  addEvent(child, 'click', function (e) {
    console.log('child');
  });


  addEvent(parent, 'click', function (e) {
    console.log('parent');
  });


  addEvent(grandparent, 'click', function (e) {
    console.log('grandparent');
  });


  addEvent(document, 'click', function (e) {
    console.log('document');
  });


  addEvent('html', 'click', function (e) {
    console.log('html');
  })


  addEvent(window, 'click', function (e) {
    console.log('window');
  })


});

addEventListener 方法有第三个可选参数,称为 useCapture,默认为 false。 如果设置为true,事件将发生在捕获阶段而不是冒泡阶段。 如果你点击子元素,它会在控制台上分别记录window、document、html、grandparent和parent。 这就是事件捕获。

7. event.preventDefault() 和 event.stopPropagation() 方法有什么区别?

event.preventDefault() 方法防止元素的默认行为。 如果在表单元素中使用,它会阻止提交。 如果在锚元素内使用,它会阻止导航。 如果在上下文菜单中使用,它会阻止其显示或出现。 

另一方面,event.stopPropagation() 方法用于防止在捕获和冒泡阶段进一步传播当前事件。

8. 如何确定元素中是否使用了 event.preventDefault() 方法?

我们可以在事件对象中使用 event.defaultPrevented 属性。 它返回一个布尔值,指示是否已在特定元素内调用 event.preventDefault()。

9. 为什么代码 obj.someprop.x 会抛出错误?

const obj = {};
console.log(obj.someprop.x);

显然,代码会抛出错误,因为我们试图访问对象中不存在的属性“someprop”中的属性“x”。 它导致未定义的值。 

请记住,访问对象本身或其原型中不存在的属性默认为未定义。 由于 undefined 没有属性“x”,因此尝试访问它会导致错误。

10.什么是event.target?

简单的说,event.target就是事件发生的元素或者触发事件的元素。

HTML:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
 </div>

JS:

function clickFunc(event) {
  console.log(event.target);
}

如果你点击一个按钮,即使我们将事件附加到最外层的 div,它仍然会打印按钮标签。 因此,我们可以得出结论,event.target 是触发事件的元素。

11.什么是event.currentTarget?

event.currentTarget 是我们明确附加事件处理程序的元素。

HTML:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
          <button style="margin:10px">
             Button
          </button>
        </div>
    </div>
 </div>

JS:

function clickFunc(event) {
  console.log(event.currentTarget);
}

如果你点击了一个按钮,即使你点击了那个按钮,它仍然会打印最外层的div标签。 在此示例中,我们可以得出结论,event.currentTarget 是事件处理程序附加到的元素。

12. 为什么在 JavaScript 中比较两个相似的对象会返回 false?

让我们从以下示例开始:

let a = { a: 1 };
let b = { a: 1 };
let c = a;
console.log(a === b); 
// It prints false even though they have the same properties.
console.log(a === c); // true

JavaScript 以不同的方式比较对象和原始类型。 在基本类型中,JavaScript 根据它们的值来比较它们,而在对象中,JavaScript 根据它们的引用或变量存储在内存中的地址来比较它们。 

这就是为什么第一个 console.log 语句返回 false,而第二个 console.log 语句返回 true。 a 和 c 具有相同的引用地址,而 a 和 b 则不同。

13. 如何计算一行中多个表达式的值?

您可以使用逗号运算符在一行中计算多个表达式的值。 它从左到右计算表达式并返回最右边的项或最后一个操作数的值。

let x = 5;


x = (x++ , x = addFive(x), x *= 2, x -= 5, x += 10);


function addFive(num) {
  return num + 5;
}

上面的结果最终得到x的值为27,首先我们将x的值自增6,然后调用addFive(6)函数,将6作为参数传给x,结果x为11 。

然后,我们将 x 的当前值乘以 2 并将其赋值给 x,将 x 的值更新为 22。

然后,我们将 x 的当前值减去 5,并将结果赋给 x,从而使 x 更新为 17。

最后,我们递增 x 乘以 10 并将更新后的值赋回给 x,导致 x 的最终值为 27。

14.什么是作用域?

JavaScript 中的作用域是指变量或函数可访问的区域。 JavaScript 具有三种作用域:全局作用域、函数作用域和块作用域 (ES6)。

全局范围:在全局命名空间中声明的变量或函数驻留在全局范围内,使它们可以从代码中的任何位置访问。

//global namespace
var g = "global";


function globalFunc(){
  function innerFunc(){
    console.log(g); // can access "g" because "g" is a global variable
  }
 innerFunc();
}

函数范围:在函数内声明的变量、函数和参数可在函数本身内访问,但不能在函数外访问。

function myFavoriteFunc(a) {
  if (true) {
    var b = "Hello " + a;
  }
  return b;
}


myFavoriteFunc("World");


console.log(a); // Throws a ReferenceError "a" is not defined
console.log(b); // does not continue here

块作用域:在块 {} 内声明的变量(使用 let 或 const)只能在该块内访问。

function testBlock(){
   if(true){
     let z = 5;
   }
   return z; 
 }


 testBlock(); // Throws a ReferenceError "z" is not defined

范围也指一组查找变量的规则。 如果在当前范围内没有找到一个变量,它会在外部范围内搜索,如果在外部范围内没有找到该变量,它将继续搜索,直到到达全局范围。 如果找到变量,就可以使用; 否则,将抛出错误。 这个搜索变量的过程也称为作用域链。

// Global scope
  var variable1 = "Comrades";   
  var variable2 = "Sayonara";


  function outer(){
  //External scopes
    var variable1 = "World";
    function inner(){
    // Internal scopes
      var variable2 = "Hello";
      console.log(variable2 + " " + variable1);
    }
    inner();
  }  
  outer(); // Hello World

15. JavaScript 中的假值是什么?

const falsyValues = ['', 0, null, undefined, NaN, false];

简而言之,假值是在转换为布尔值时变为假的值。

要检查一个值是否为假,您可以使用布尔函数或 !! 操作员。

16. JavaScript 中“this”的值是多少?

基本上,this 指的是当前正在执行或调用函数的对象的值。 this 的价值根据我们使用它的上下文和我们使用它的地方而变化。

const carDetails = {
  name: "maxwell",
  yearBought: 2023,
  getName(){
    return this.name;
  },
  isRegistered: true
};
console.log(carDetails.getName()); // maxwell

这通常是我们所期望的,因为在我们返回 this.name 的 getName 方法中。 在此上下文中,this 指的是 carDetails 对象,它是执行函数的当前“所有者”对象。

接下来我们做一些奇怪的事情:

var name = "maxwell";
var getCarName = carDetails.getName;


console.log(getCarName()); // maxwell

它在上面打印了‘maxwell’,这很奇怪,因为第一个 console.log 语句打印了‘maxwell’。 这样做的原因是 getCarName 方法有一个不同的“所有者”对象,即窗口对象。 

在全局范围内使用 var 关键字声明变量时,它们将作为属性附加到具有相同变量名的 window 对象。 

请注意,当不使用“use strict”时,全局范围内的 this 指的是 window 对象。

console.log(getCarName === window.getCarName); // true
console.log(getCarName === this.getCarName); // true

在这个例子中,this 和 window 指的是同一个对象。

解决此问题的一种方法是在函数中使用 apply 和 call 方法。

console.log(getCarName.apply(carDetails)); 
console.log(getCarName.call(carDetails));

apply 和 call 方法期望第一个参数是一个对象,这将是函数内部 this 的值。

IIFE 或立即调用函数表达式、在全局范围内声明的函数、对象方法中的匿名函数和内部函数都具有指向窗口对象的默认值 this。

(function (){
     console.log(this);
   })(); 


   function iHateThis(){
      console.log(this);
   }


   iHateThis();


   const myFavoriteObj = {
     guessThis(){
        function getName(){
          console.log(this.name);
        }
        getName();
     },
     name: 'Maxwell',
     thisIsAnnoying(callback){
       callback();
     }
   };




   myFavoriteObj.guessThis(); 
   myFavoriteObj.thisIsAnnoying(function (){
     console.log(this); 
   });

如果我们想获取myFavoriteObj对象中name属性(Maxwell)的值,有两种方法可以解决这个问题。

一种方法是将 this 的值保存在变量中。

const myFavoriteObj = {
 guessThis(){
  const self = this; // Store the value of this in the self variable
  function getName(){
    console.log(self.name);
  }
  getName();
 },
 name: 'Marko Polo',
 thisIsAnnoying(callback){
   callback();
  }
};

第二种方法是使用箭头函数。

const myFavoriteObj = {
  guessThis(){
     const getName = () => { 
       console.log(this.name);
     }
     getName();
  },
  name: 'Maxwell',
  thisIsAnnoying(callback){
   callback();
  }
};

箭头函数没有自己的 this。 它们从封闭的词法范围继承 this 值。 在此示例中,this 值是 myFavoriteObj 对象,它位于 getName 内部函数之外。

17. ‘var’、‘let’和‘const’有什么区别?

用 var 声明的变量被提升并附加到 window 对象,而用 let 和 const 声明的变量则不会。

var a = 100;
console.log(a,window.a);    // 100 100


let b = 10;
console.log(b,window.b);    // 10 undefined


const c = 1;
console.log(c,window.c);    // 1 undefined

用 var 声明的变量会进行变量提升,而用 let 和 const 声明的变量不会被提升。

console.log(a); // undefined  ===>  a has been declared and not assigned a value, the default is undefined
var a = 100;


console.log(b); // error:b is not defined 
let b = 10;


console.log(c); // error:c is not defined 
const c = 10;

let 和 const 声明创建块作用域。

if(1){
  var a = 100;
  let b = 10;
}


console.log(a); // 100
console.log(b)  // error:b is not defined  


-------------------------------------------------------------


if(1){
  var a = 100;
  const c = 1;
}
console.log(a); // 100
console.log(c)  // error:c is not defined

在同一范围内,let 和 const 不能声明同名变量,而 var 允许。

var a = 100;
console.log(a); // 100


var a = 10;
console.log(a); // 10
-------------------------------------
let a = 100;
let a = 10;


//  error:Identifier 'a' has already been declared

const

/*
*   1、Once declared, it must be assigned a value and cannot be used null .
*
*   2、Cannot be modified after declaration
*
*   3、If the declaration is for a composite data type, its properties can be modified.
*
* */


const a = 100; 


const list = [];
list[0] = 10;
console.log(list);  // [10]


const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj);  // {a:10000,name:'apple'}

18. 什么是箭头函数?

与函数表达式相比,箭头函数表达式具有更简洁的语法,并且没有自己的 this、arguments、super 或 new.target。 箭头函数表达式更适合原本使用匿名函数的情况,不能作为构造函数使用。

//ES5 Version
var getCurrentDate = function (){
  return new Date();
}


//ES6 Version
const getCurrentDate = () => new Date();

在这个例子中,在 ES5 版本中,我们有 function(){} 声明和 return 关键字,它们分别是创建函数和返回值所必需的。 

在箭头函数版本中,我们只需要括号 () 而不需要 return 语句,因为如果我们只有一个表达式或值要返回,则箭头函数具有隐式返回。

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '!';
}


//ES6 Version
const greet = (name) => `Hello ${name}`;
const greet2 = name => `Hello ${name}`;

我们还可以在箭头函数中使用与函数表达式和函数声明相同的参数。 如果我们在箭头函数中有一个参数,我们可以省略括号。

const getArgs = () => arguments


const getArgs2 = (...rest) => rest

箭头函数无法访问参数对象。 因此,调用第一个 getArgs 函数会抛出错误。 相反,我们可以使用剩余参数来获取箭头函数中传递的所有参数。

const data = {
  result: 0,
  nums: [1, 2, 3, 4, 5],
  computeResult() {
    // Here "this" refers to the "data" object
    const addAll = () => {
      return this.nums.reduce((total, cur) => total + cur, 0)
    };
    this.result = addAll();
  }
};

箭头函数没有自己的 this 值。 它们捕获词法作用域函数的 this 值。 在此示例中,addAll 函数将从 computeResult 方法继承 this 值。 如果我们在全局范围内声明一个箭头函数,则 this 值将是 window 对象。

19. 什么是对象的原型?

简单来说,原型就是一个对象的蓝图。 如果它存在于当前对象中,则用作属性和方法的回退。 它是对象之间共享属性和功能的一种方式,也是 JavaScript 中实现继承的核心。

const o = {};
console.log(o.toString()); // logs [object Object]

即使 o 对象没有 o.toString 方法,它也不会抛出错误而是返回字符串 [object Object]。 

当一个对象中没有找到某个属性时,它会在它的原型中寻找它,如果仍然不存在,它会继续在原型的原型中寻找,以此类推,直到找到同名的属性 在原型链中。 

原型链的末端是 Object.prototype。

console.log(o.toString === Object.prototype.toString); // logs true

20. 什么是 IIFE,它的目的是什么?

IIFE 或立即调用函数表达式,是在创建或声明后立即调用或执行的函数。 创建 IIFE 的语法是将函数 (){} 包裹在圆括号 () 中,然后用另一对圆括号 () 调用它,例如 (function(){})()。

(function(){
  ...
} ());


(function () {
  ...
})();


(function named(params) {
  ...
})();


(() => {


});


(function (global) {
  ...
})(window);


const utility = (function () {
  return {
    ...
  }
})

所有这些示例都是有效的 IIFE。 倒数第二个示例演示了我们可以将参数传递给 IIFE 函数。 最后一个示例表明我们可以将 IIFE 的结果保存到一个变量中供以后使用。

IIFE 的主要目的之一是避免命名冲突或全局范围内的变量污染全局命名空间。 这是一个例子:

<script src="https://cdnurl.com/somelibrary.js"></script>

假设我们已经导入了一个指向 omelibr.js 的链接,它提供了我们在代码中使用的一些全局函数。 但是,这个库有两个方法,createGraph 和 drawGraph,我们没有使用它们,因为它们有错误。 我们要实现自己的 createGraph 和 drawGraph 方法。

解决这个问题的一种方法是直接覆盖它们。

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function createGraph() {
      // createGraph logic here
   }
   function drawGraph() {
      // drawGraph logic here
   }
</script>

当我们使用这个解决方案时,我们重写了库提供的两个方法。

另一种方法是我们自己重命名它们。

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   function myCreateGraph() {
      // createGraph logic here
   }
   function myDrawGraph() {
      // drawGraph logic here
   }
</script>

当我们使用这个解决方案时,我们将函数调用更改为新的函数名称。

另一种方法是使用 IIFE

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
   const graphUtility = (function () {
      function createGraph() {
         // createGraph logic here
      }
      function drawGraph() {
         // drawGraph logic here
      }
      return {
         createGraph,
         drawGraph
      }
   })
</script>

在这个解决方案中,我们声明了一个变量 graphUtility 来存储 IIFE 执行的结果。 该函数返回一个包含两个方法的对象,createGraph 和 drawGraph。

IIFE 也可以用来解决常见的面试问题。

var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
   li[i].addEventListener('click', function (e) {
      console.log(i);
   })

假设我们有一个类为 list-group 的 <ul> 元素,它有 5 个 <li> 子元素。 当我们点击一个单独的 <li> 元素时,我们想要打印它对应的索引值。 但是,上面的代码没有按预期工作。 在这种情况下,每次我们单击 <li> 时,它都会将 i 的值打印为 5。这是由于闭包造成的。

闭包只是函数从其当前作用域、父函数作用域和全局作用域记住变量的能力。 当我们在全局范围内使用 var 关键字声明一个变量时,我们创建了一个全局变量 i。 因此,当我们点击 <li> 元素时,它会打印 5,因为这是稍后在回调函数中引用 i 时的值。

使用 IIFE 可以解决这个问题。

var li = document.querySelectorAll('.list-group > li');
for (var i = 0, len = li.length; i < len; i++) {
   (function (currentIndex) {
      li[currentIndex].addEventListener('click', function (e) {
         console.log(currentIndex);
      })
   })(i);
}

该解决方案之所以有效,是因为 IIFE 为每次迭代创建了一个新范围。 我们捕获 i 的值并将其作为 currentIndex 参数传递,因此当调用 IIFE 时,每次迭代都有不同的 currentIndex 值。

21. Function.prototype.apply 方法的目的是什么?

apply() 方法调用具有给定 this 值的函数,并将参数作为数组(或类似数组的对象)提供。

const details = {
  message: 'Hello medium!'
};


function getMessage(){
  return this.message;
}


getMessage.apply(details); // 'Hello medium!'

call() 方法的用途与 apply() 方法类似,区别在于 call() 接受参数列表,而 apply() 接受参数数组。

const person = {
  name: "Maxwell"
};


function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}


greeting.apply(person, ['Hello']); // "Hello Maxwell!"

22. Function.prototype.call 方法的作用是什么?

call() 方法用于调用具有指定 this 值和提供的各个参数的函数。

const details = {
  message: 'Hello Medium!'
};


function getMessage(){
  return this.message;
}


getMessage.call(details); // 'Hello Medium!'

注意:此方法的语法和用途类似于 apply() 方法,唯一的区别是 call() 接受参数列表,而 apply() 接受包含多个参数的数组。

const person = {
  name: "Maxwell"
};


function greeting(greetingMessage) {
  return `${greetingMessage} ${this.name}`;
}


greeting.call(person, 'Hello'); // "Hello Maxwell!"

23. Function.prototype.apply 和 Function.prototype.call 有什么区别?

apply() 方法可以调用具有指定 this 值和参数数组(或类数组对象)的函数或方法。 call() 方法类似于 apply(),唯一的区别是 call() 接受参数作为参数列表。

const obj1 = {
 result:0
};


const obj2 = {
 result:0
};


function reduceAdd(){
   let result = 0;
   for(let i = 0, len = arguments.length; i < len; i++){
     result += arguments[i];
   }
   this.result = result;
}


reduceAdd.apply(obj1, [1, 2, 3, 4, 5]); // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5); // 15

24. Function.prototype.bind() 的作用是什么?

bind() 方法创建一个新函数,在调用时将其 this 值设置为 bind() 的第一个参数,后续参数在调用时作为新函数的参数提供。

import React from 'react';


class MyComponent extends React.Component {
     constructor(props){
          super(props); 
          this.state = {
             value : ""
          }  
          this.handleChange = this.handleChange.bind(this); 
     }


     handleChange(e){
       //do something amazing here
     }


     render(){
        return (
              <>
                <input type={this.props.type}
                        value={this.state.value}
                     onChange={this.handleChange}                      
                  />
              </>
        )
     }
}

25. 什么是高阶函数?

高阶函数简单来说就是将函数作为参数或返回函数的函数。

function higherOrderFunction(param,callback){
    return callback(param);
}

26. 什么是“arguments”对象?

arguments 对象是作为参数传递给函数的值的集合。 它是一个类似数组的对象,因为它具有“长度”属性,并且可以使用数组索引符号(如 arguments[1])访问各个值。 但是,它没有内置的数组方法,如 forEach、reduce、filter 和 map。

我们可以使用 Array.prototype.slice 将参数对象转换为数组。

function one() {
  return Array.prototype.slice.call(arguments);
}

注意:箭头函数没有参数对象。

function one() {
  return arguments;
}
const two = function () {
  return arguments;
}
const three = function three() {
  return arguments;
}


const four = () => arguments;


four(); // Throws an error  - arguments is not defined

当我们调用函数四时,它会抛出 ReferenceError: arguments is not defined 错误。 可以使用 rest 语法解决此问题。

const four = (...args) => args;

这将自动将所有参数值放入一个数组中。

27.如何创建没有原型的对象?

我们可以使用 Object.create 方法来创建一个没有原型的对象。

const o1 = {};
console.log(o1.toString()); // [object Object]


const o2 = Object.create(null);
console.log(o2.toString());
// throws an error o2.toString is not a function

28.什么是class?

class 是一种在 JavaScript 中编写构造函数的新方法。 它是构造函数的语法糖,在底层仍然使用原型和基于原型的继承。

//ES5 Version
   function Person(firstName, lastName, age, address){
      this.firstName = firstName;
      this.lastName = lastName;
      this.age = age;
      this.address = address;
   }


   Person.self = function(){
     return this;
   }


   Person.prototype.toString = function(){
     return "[object Person]";
   }


   Person.prototype.getFullName = function (){
     return this.firstName + " " + this.lastName;
   }  


   //ES6 Version
   class Person {
        constructor(firstName, lastName, age, address){
            this.lastName = lastName;
            this.firstName = firstName;
            this.age = age;
            this.address = address;
        }


        static self() {
           return this;
        }


        toString(){
           return "[object Person]";
        }


        getFullName(){
           return `${this.firstName} ${this.lastName}`;
        }
   }

覆盖方法并从另一个类继承。

//ES5 Version
Employee.prototype = Object.create(Person.prototype);


function Employee(firstName, lastName, age, address, jobTitle, yearStarted) {
  Person.call(this, firstName, lastName, age, address);
  this.jobTitle = jobTitle;
  this.yearStarted = yearStarted;
}


Employee.prototype.describe = function () {
  return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
}


Employee.prototype.toString = function () {
  return "[object Employee]";
}


//ES6 Version
class Employee extends Person { //Inherits from "Person" class
  constructor(firstName, lastName, age, address, jobTitle, yearStarted) {
    super(firstName, lastName, age, address);
    this.jobTitle = jobTitle;
    this.yearStarted = yearStarted;
  }


  describe() {
    return `I am ${this.getFullName()} and I have a position of ${this.jobTitle} and I started at ${this.yearStarted}`;
  }


  toString() { // Overriding the "toString" method of "Person"
    return "[object Employee]";
  }
}

那么我们怎么知道它在内部使用了原型呢?

class Something {


}


function AnotherSomething(){


}
const as = new AnotherSomething();
const s = new Something();


console.log(typeof Something); // "function"
console.log(typeof AnotherSomething); // "function"
console.log(as.toString()); // "[object Object]"
console.log(as.toString()); // "[object Object]"
console.log(as.toString === Object.prototype.toString); // true
console.log(s.toString === Object.prototype.toString); // true

29.什么是模板字符串?

模板字符串是一种在 JavaScript 中创建字符串的新方法。 我们可以使用反引号 (`) 使字符串成为模板字符串。

//ES5 Version


var greet = 'Hi I\'m Mark';


//ES6 Version
let greet = `Hi I'm Mark`;

在 ES5 中,我们需要使用转义字符来实现多行效果,但是有了模板字符串,我们就不需要经历那些麻烦了。

//ES5 Version
var lastWords = '\n'
  + '   I  \n'
  + '   Am  \n'
  + 'Iron Man \n';




//ES6 Version
let lastWords = `
    I
    Am
  Iron Man   
`;

在 ES5 版本中,我们需要添加 \n 来在字符串中添加新行。 在模板字符串中,我们不需要这样做。

//ES5 Version
function greet(name) {
  return 'Hello ' + name + '!';
}




//ES6 Version
function greet(name) {
  return `Hello ${name} !`;
}

在 ES5 版本中,如果我们需要在字符串中包含表达式或值,我们必须使用 + 运算符。 在模板字符串中,我们可以使用 ${expr} 来嵌入一个表达式,这使得它比 ES5 版本更干净。

30.什么是对象解构?

对象解构是一种新的、更简洁的从对象或数组中提取值的方法。 假设我们有以下对象:

const employee = {
  firstName: "Marko",
  lastName: "Polo",
  position: "Software Developer",
  yearHired: 2017
};

以前,要从对象中提取属性,传统的方法是创建一个与对象属性同名的变量。 这种方法很麻烦,因为我们必须为每个属性创建一个新变量。 假设我们有一个包含许多属性和方法的大对象,使用这种方法提取属性会很麻烦。

var firstName = employee.firstName;
var lastName = employee.lastName;
var position = employee.position;
var yearHired = employee.yearHired;

使用解构语法使其更加简洁:

{ firstName, lastName, position, yearHired } = employee;

我们还可以为属性分配别名:

let { firstName: fName, lastName: lName, position, yearHired } = employee;

当然,如果属性值未定义,我们也可以指定一个默认值:

let { firstName = "Mark", lastName: lName, position, yearHired } = employee;

总结

以上就是我们今天这篇文章想与你分享的全部内容,希望对你有用。

最后,感谢你的阅读。

学习更多技能

请点击下方公众号

445eb4f6ff5303cbcd83430e0f3a8500.gif

猜你喜欢

转载自blog.csdn.net/Ed7zgeE9X/article/details/130998341