// "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

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

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError

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);
  // ...
}

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

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

function abc(){}

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

// 我们可以在这里调用
abc(); 

// 在这里定义
function abc(){}

// 也可以在这里调用 
abc(); 

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

// 我们可以在这里调用
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()的情况下arguments.callee,—取决于浏览器。

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

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

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);

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

var obj = {
  myProperty: 1    
}
console.log(obj.hasOwnProperty('myProperty')) // true
delete obj.myProperty
console.log(obj.hasOwnProperty('myProperty')) // false

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

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

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

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

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

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

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

下面是一些事例:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' trn ' == 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

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

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

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

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

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

使用==运算符(等于)

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

使用===操作符

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

9.在 中深拷贝一个对象的最有效方法是什么?

快速克隆,数据丢失– JSON.parse/

如果您没有在对象中使用Date、函数、、、、Map、Set、blob、、稀疏数组、类型化数组或其他复杂类型,那么可以使用一行简单代码来深拷贝一个对象:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(), 
  undef: undefined,  // 丢失
  inf: Infinity,  // 被设置为 null
  re: /.*/,  // 丢失
}
console.log(a);
console.log(typeof a.date);  // object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
/*
object
{
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: '2020-09-04T00:45:41.823Z',
  inf: null,
  re: {}
}

*/
console.log(typeof clone.date);  // string

使用库进行可靠的克隆

由于克隆对象不是一件简单的事情(复杂类型、循环引用、函数等等),大多数主要的库都提供了拷贝对象的函数。如果你已经在使用一个库,请检查它是否具有对象克隆功能。例如

ES6

ES6 提供了两种浅拷贝机制:.()和语法。它将所有可枚举的自有属性的值从一个对象复制到另一个对象。例如

var A1 = {a: "2"};
var A2 = Object.assign({}, A1);
var A3 = {...A1};  // Spread Syntax

在以前的测试中,速度是最主要的问题

JSON.parse(JSON.stringify(obj))

这是深拷贝对象的最慢方法,它比.慢 10-20%。

当deep标志设置为false(浅克隆)时,.非常快。这是一个不错的选择,因为它包括一些用于类型验证的额外逻辑arguments.callee,并且不会复制未定义的属性等,但这也会使你的速度变慢。

如果想拷贝的一个对象且你知道对象结构。那么,你可以写一个简单的for (var i in obj)循环来克隆你的对象,同时检查,这将比快得多。

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

注意在 Date 对象JSON上使用JSON.parse(JSON.(obj))方法。JSON.(new Date())以ISO格式返回日期的字符串表示,JSON.parse()不会将其转换回Date对象。

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

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

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

ES6

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

使用.json:

{
    "type""module"
}

在 .js: 中

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

main.js:

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

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

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

在main.mjs 中

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

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

<script type="module">
  import { hello } from './hello.mjs'; // Or it could be simply `hello.js`
  hello('world');

// 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);
}

浏览器中的动态导入

动态导入允许脚本根据需要加载其他脚本

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });

Node.js

在 Node.js 中用的较多还是 ./

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}

// .js const = ('./'); let val = .hello(); // val is "Hello"

动态加载文件

我们可以通过动态创建 来动态引入文件:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script"); 

    document.head.appendChild(script); 
}

检测脚本何时执行

现在,有一个个大问题。上面这种动态加载都是异步执行的,这样可以提高网页的性能。这意味着不能在动态加载下马上使用该资源,因为它可能还在加载。

例如:.js包含:

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

然后,按F5重新加载页面,可能就有效了。那么该怎么办呢?

我们可以使用回调函数来解决些问题。

function loadScript(url, callback)
{
    var head = document.head;
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    script.onload = callback;

    head.appendChild(script);
}

然后编写在函数中加载脚本后要使用的代码

var myPrettyCode = function() {
   // Here, do whatever you want
};

然后,运行代码:

loadScript("my_lovely_script.js", myPrettyCode);

请注意,脚本可能在加载DOM之后或之前执行,具体取决于浏览器以及是否包括行.async = false;。

包邮送书

jscallee_arguments.callee_callee

《编程精解(第3版)》

马尔奇·哈弗贝克()著

卢涛,李颖 译

《编程精解》第3版包含了语言ES6 规范的最新功能,比如绑定、常量、类、等。通过学习本书,你能了解该语言的最新发展,并编写出更强大的代码。适合零基础入门的新手阅读。

本书特色:

● 基于ES6:

针对语言ES6 规范的最新功能更新,助你了解语言的新发展,编写出更强大的代码

● 注重实战:

通过投递机器人、编程语言、平台交互游戏、像素绘图程序、动态网站5个实战章节,教你快速上手实际的 项目

● 学练结合:

每章配有示例程序和习题和答案,帮你进行知识回顾

● 线上资源:

本书配套功能丰富的支持网站,可实际执行内嵌的代码段,及时查看结果

如果你是零基础入门初学者,时间又不充裕,想快速入门,小编建议你可以先读这本《编程精解》第3版。毕竟本书相比其他几本“板砖”书,实在是轻薄了不少,你可以快速读完整本书,迅速掌握核心技术。

凭手气抽奖(3本)

公众号后台回复"222"就可以获取得程序抽奖码,为了避免中奖后失联,提前加我微信哈,开奖时间: 周日(10/04) 19:00

留言抽奖(2本)

大奖:留言点赞数「前2名」每人可获得一本。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注