From:菜鸟教程 JavaScript:https://www.runoob.com/
W3School JavaScript 教程

廖雪峰官网 JavaScript 教程:https://www.liaoxuefeng.com/wiki/1022910821149312
Node.js 教程:http://www.runoob.com/nodejs/nodejs-tutorial.html
JavaScript 特效:https://www.cnblogs.com/xiaoyunxiao/archive/2012/04/04/2431583.html

前言

JavaScript 是能够运行在浏览器上的脚本语言. 简称JS,Javascript 名字中包含 Java 但是两者之间没有任何关系。纯粹是商业碰瓷.、

  • JavaScript 是可以操作 Web 标签的编程语言,也是一种轻量级的编程语言。所有现代的 HTML 页面都使用 JavaScript。
  • 在 HTML 页面中使用 <script> 和 </script> 插入需要执行的 JavaScript 代码。
  • JavaScript 插入 HTML 页面后,可以在 HTML 页面的任何地方调用 JS 函数 (必须用英文双引号 "js函数" 或者单引号 'js函数' 包括),然后由所有的现代浏览器执行JavaScript 语句是发给浏览器的命令。这些命令的作用是告诉浏览器要做的事情。
            HTML事件是发生在 HTML 元素上的事情。通过 HTML 事件 可以绑定 JavaScript。
            HTML 事件可以是浏览器行为,也可以是用户行为。
            HTML 事件的部分实例:HTML 页面完成加载、HTML input 字段改变时、HTML 按钮被点击 等。HTML 事件属性https://www.runoob.com/tags/ref-eventattributes.html
            通常,当事件发生时,你可以做些事情。即事件触发时 JavaScript 可以执行一些代码。HTML 元素中可以添加事件属性。
            单引号:<some-HTML-element some-event='JavaScript 代码'>
            双引号:<some-HTML-element some-event="JavaScript 代码">
  • 经常看到 document.getElementById("some id")。这个方法是 HTML DOM 中定义的。DOM (Document Object Model)(文档对象模型)是用于访问 HTML 元素的正式 W3C 标准。
  • HTML 中的 js 脚本必须位于 <script> 与 </script> 标签之间。但是 js 脚本的调用可以在HTML 页面的任何部分。
  • JavaScript 对大小写敏感。函数 getElementById 与 getElementbyID 是不同的。同样,变量 myVariable 与 MyVariable 也是不同的。

既然 JS 是可以运行在浏览器上的脚本,浏览器又是执行HTML程序的,那么如何在HTML中引入JS

  • 方案 1:直接在<script>标签中引入编写js代码
  • 方案 2:将js代码写在js文件中, 然后通过script标签的src属性进行引入

两种方式运行的效果是一致的。但是需要注意:HTML程序在执行的时候是从上到下进行渲染的。

示例:

<html>
<head>
</head>
<body>
<h1 style="text-align: center;">通过HTMl测试JavaScript执行</h1>
</body>
   <script>
        function print_date_time() {
            let now = new Date();
            // 创建一个DateTimeFormat对象,用于格式化日期
            // 第一个参数是语言环境,第二个参数是格式化选项
            let dateFormatter = new Intl.DateTimeFormat('en-US', {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit',
                hour12: false  // 使用24小时制
            });
            // 使用format()方法来格式化日期
            let formattedDate = dateFormatter.format(now);
            console.log(formattedDate);
            return formattedDate;
        }
        let newElement = document.createElement("p");
        // 为新元素添加一些内容
        newElement.textContent = print_date_time();
        // 将新元素添加到<body>标签的末尾
        document.body.appendChild(newElement);
        // 直接向<body>标签的末尾添加新的HTML内容
        document.body.innerHTML += "<p>这是一个新的段落。</p>";
    </script>
</html>
<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>
	
<h1>我的第一个 Web 页面</h1>
<p>我的第一个段落。</p>
<button onclick="myFunction()">点我_1</button>
<button onclick="window.alert('test');">点我_2</button>
<script>
	function myFunction(){document.write(Date());}
</script>

</body>
</html>

HTML 输出流中使用 document.write,相当于添加在原有html代码中添加一串html代码。而如果在文档加载后使用(如使用函数),会覆盖整个文档。

使用函数来执行 document.write 代码如下:

<script>
function myfunction(){
    document.write("使用函数来执行doucment.write,即在文档加载后再执行这个操作,会实现文档覆盖");
}
document.write("<h1>这是一个标题</h1>");
document.write("<p>这是一个段落。</p>");
</script>
<p >
只能在 HTML 输出流中使用 <strong>document.write</strong>。
如果在文档已加载后使用它(比如在函数中),会覆盖整个文档。
</p>
<button type="button" onclick="myfunction()">点击这里</button>

在JavaScript中,你可以使用几种不同的方法来设置HTML元素的内容。常用的有 innerHTML、textContent 和 innerText 属性。每种方法有其特定的使用场景和行为。

  • innerHTML属性可以获取或设置HTML元素的HTML或XML内容。如果设置了元素的innerHTML,那么包含的字符串可以包含HTML标签,这些标签将被解析并作为DOM的一部分渲染。注意:由于innerHTML解析HTML标记,如果内容来自用户输入,使用它可能会产生安全问题,例如XSS攻击。
  • textContent属性提供了一种获取或设置节点及其后代的文本内容的方法。与innerHTML不同,如果你设置了元素的textContent,提供的字符串将不会被解析为HTML。它仅仅是文本。
  • innerText属性也类似于textContent,但它会考虑CSS样式(例如隐藏的元素将不包括在innerText中,而在textContent中会)。innerText比textContent更耗费资源,因为它需要重新计算样式。

总结:

  • 如果你需要设置的内容包含HTML标签,并且你信任这些HTML内容(它们不是用户输入),你可以使用innerHTML
  • 如果你仅需要设置文本,并保证它按原样显示而不被作为HTML解析,使用textContent更安全。
  • 如果你希望设置的文本显示与CSS样式相关(如隐藏元素不显示),可以使用innerText,但要注意其性能影响。

Window 对象

window对象:window对象表示一个浏览器窗口或一个框架。在客户端JavaScript中,window对象是全局对象,所有的表达式都在当前的环境中计算。也就是说,要引用当前窗口根本不需要特殊的语法,可以把那个窗口的属性当作全局变量来使用。例如,可以只写document,而不必写window.document。

  • 所有浏览器都支持 window 对象。它表示浏览器窗口。
  • 所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。
  • 全局变量是 window 对象的属性。
  • 全局函数是 window 对象的方法。
        甚至 HTML DOM 的 document 也是 window 对象的属性之一:
                window.document.getElementById("header");
        与此相同:
                document.getElementById("header");

JS 中的概念区分

1、global (全局) 对象

  • ①JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。
  • ② 全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局对象不是任何对象的属性,所以它没有名称。
  • 在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量,都将成为全局对象的属性。
  • 全局对象只是一个对象,而不是类。既没有构造函数,也无法用new实例化一个新的全局对象。
  • ⑤ 实际上,ECMAScript 标准没有规定全局对象的类型,而在客户端 JavaScript 中,全局对象就是 Window 对象,即运行 JavaScript 代码的 Web 浏览器窗口浏览器把全局对象作为 window 对象 的一部分实现了,因此,所有的全局属性和函数都是window对象的属性和方法。 

通俗化理解:

  • 《JavaScript高级程序设计》中谈到,global 对象可以说是 ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。从某种意义上讲,它是一个终极的“兜底儿对象”,换句话说呢,就是不属于任何其他对象的属性和方法,最终都是它的属性和方法。我理解为,这个global对象呢,就是整个JS的“老祖宗”,找不到归属的那些“子子孙孙”都可以到它这里来认祖归宗。所有在全局作用域中定义的属性和函数,都是global对象的属性和方法,比如isNaN()、parseInt()以及parseFloat()等,实际都是它的方法;还有就是常见的一些特殊值,如:NaN、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法。总之,记住一点:global对象就是“老祖宗”,所有找不到归属的就都是它的。

注意:

  • "global" 这个单词本身是不可以直接访问的,本身就是一个概念,就是中文 "全局" 的英文原义,"Global Object" 翻译就是 "全局对象" 或 "global对象"。比如:global.Math.abs(1) 就是错误的,访问全局对象的属性时往往省略前缀,如:Math.abs(1) 就行了。而 window对象 是浏览器的JavaScript中具体存在的一个全局对象,是可以直接访问的。

示例 1: ( test.html ):

<script type="text/javascript">
    console.log(this);
</script>

<body>
    <h1>console.log(this);</h1>
</body>

打开 chrome 浏览器的 控制台,看到输出什么了吗?

window对象! 因为在全局作用域下,this 指向全局对象。在浏览器中全局对象就是 window 对象。

示例 2:

<script type="text/javascript">
    console.log(window); 
</script>

<body>
    <h1>console.log(window);</h1>
</body>

打开 chrome 浏览器的控制台:

输出 window对象,说明 "window"这个单词在浏览器JS中代表的就是一个真实存在的全局的对象,可以直接访问window这对象的。

示例 3:

<script type="text/javascript">
    console.log(global); 
</script>

<body>
    <h1>console.log(global);</h1>
</body>

运行结果:

显示 "Uncaught ReferenceError:global is not defined"。即 global 未定义,说明 "global" 这个单词不是一个默认的全局对象,不能在JS中直接访问的。正如前面所说,“global”只是中文”全局“的英语原义,不是一个对象,而是一个概念。

global 的属性和方法:http://blog.csdn.net/qq_32758013/article/details/74781788

2、window 对象

在全局作用域中声明的变量、函数都是window对象的属性和方法。

window对象 是相对于web浏览器而言的,依赖于浏览器,在浏览器中全局对象指的就是window对象,可以说window对象是全局对象在浏览器中的具体表现。

window 并不是ECMAScript规定的内置对象。内置对象的定义是:“由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。”  而 window对象就是宿主对象。而全局对象是在任何环境中都存在的。window对象具体也就是指浏览器打开的那个窗口。如果HTML文档包含框架(frame 或 iframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象。

因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。

来看下面的例子:

<script type="text/javascript">
    var name = "chunlynn";
    var color = "green"
    function sayName(){
        console.log(name);
    }
    function sayColor(){
        console.log(window.color);
    }
    sayName();         // output:chunlynn
    window.sayName();  // output:chunlynn
    sayColor();        // output:green
    window.sayColor(); //output:green
</script>


<body>
    <h1>"Test"</h1>
</body>

打开 chrome 浏览器的控制台:

这里定义了一个名为color 的全局变量和一个名为sayColor()的全局函数。在sayColor()内部,我们通过window.color 来访问color 变量,说明全局变量是window 对象的属性。然后又使用window.sayColor()来直接通过window 对象调用这个函数,说明全局作用域中声明的函数是window对象的方法。window对象一般是省略的

window 对象的属性和方法:http://www.cnblogs.com/zengwei/archive/2007/11/02/946520.html

window的属性和方法比较多,这个链接可能说的不全仅供参考,具体的请在 chrome 后台中,输入window查看

3、document 对象

document 对象是 window 对象的一个属性,是显示于窗口内的一个文档。而 window 对象则是一个顶层对象,它不是另一个对象的属性。

document 可以理解为文档,就是你的网页,而 window 是你的窗口,就是你的浏览器包含的。

区别:

<html>
<head>
    <script>
        // window代表浏览器一个tab页的整个web页面
        window.onload = function () {
            let tag_h = document.createElement('button');
            tag_h.innerText = "通过 JavaScript 添加button";
            tag_h.addEventListener("click", function () {
                alert("按钮被点击");
            });
            document.body.appendChild(tag_h);
        }
    </script>
</head>
<body>
<h1 style="text-align: center;">通过HTMl测试JavaScript执行</h1>
</body>
</html>

在各种 JavaScript 运行环境中取得全局对象 global 的方法

From:https://www.cnblogs.com/emmet7life/p/7691681.html

ES5 的顶层对象,本身也是一个问题,因为它在各种实现里面是不统一的。

  • 浏览器里面,顶层对象是 window,但 Node 和 Web Worker 没有window。
  • 浏览器和 Web Worker 里面,self 也指向顶层对象,但是 Node 没有self。
  • Node 里面,顶层对象是 global,但其他环境都不支持。

同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用 this 变量,但是有局限性。

  • 全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
  • 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。
  • 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了CSP(Content Security Policy,内容安全政策),那么eval、new Function这些方法都可能无法使用。

综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法。

// 方法一
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);
 
// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

现在有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。

垫片库system.global模拟了这个提案,可以在所有环境拿到global

// CommonJS 的写法
require('system.global/shim')();
 
// ES6 模块的写法
import shim from 'system.global/shim'; shim();

上面代码可以保证各种环境里面,global对象都是存在的。

// CommonJS 的写法
var global = require('system.global')();
 
// ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();

上面代码将顶层对象放入变量 global


 

JavaScript 输出

JavaScript 可以通过不同的方式来打印数据:

  • 使用 window.alert() 弹出警告框。
  • 使用 document.write() 方法将内容写到 HTML 文档中。
  • 使用 innerHTML 写入到 HTML 元素。
  • 使用 console.log() 写入到浏览器的控制台。

JavaScript 变量

JavaScript 变量均为 对象。当声明一个变量时,就创建了一个新的对象。 JavaScript  一切皆对象。

声明 (创建):var、let、const

在 JavaScript 中创建变量通常称为"声明"变量。使用 var 关键词来声明变量:var carname;
变量声明之后,该变量是空的(它没有值)。如需向变量赋值,使用等号:carname="Volvo";
也可以在声明变量时对其赋值:var carname="Volvo"; 示例:创建名为 carname 的变量,并向其赋值 "Volvo",然后把它放入 id="demo" 的 HTML 段落中:

var carname="Volvo";
document.getElementById("demo").innerHTML=carname;

let 变量 = ""; //当前代码块生效
const 常量 = "123456"; //被const创建出来的东西不可以被修改

var 和 let 区别。在JavaScript中,var 和 let 都用于声明变量,但它们之间存在几个关键的区别。

作用域:

  • var 声明的变量 作用域是分情况的:
            如果在一个函数内部声明,则具有函数作用域,它仅在该函数整个内部可见。
            如果在函数外部声明,它就在全局作用域内可见。
  • let 声明的变量:
        具有块级作用域,即它们只在包含它们的代码块(例如:if语句、循环体)内有效。

提升(Hoisting):

  • var声明的变量会被提升到它们所在作用域的顶部,也就是说在声明之前就可以访问这些变量,但是访问的结果是undefined
  • let声明的变量同样会在它们的作用域内被提升,但是不允许在声明之前访问它们(这会导致一个ReferenceError),这种行为被称为暂时性死区(Temporal Dead Zone,TDZ)。

重复声明:

  • 在相同的作用域内,用var声明的变量可以被重新声明。
  • let声明的变量则不允许在相同的作用域内重复声明,尝试这样做会引发错误。

全局对象属性:

  • 在全局作用域中,用var声明的变量会成为全局对象的属性(在浏览器中是window对象,在Node.js中是global对象)。
  • let声明的变量不会成为全局对象的属性。

考虑到var的一些特性(如变量提升和较宽松的作用域)可能引发意外的行为,现代JavaScript开发中优先使用let(和const,对于不需要重新赋值的变量)来声明变量,以促进更加可靠和可预测的代码编写风格。为什么建议使用let?

  • 减少错误let的块级作用域和暂时性死区特性有助于减少因变量提升或不小心的变量重复声明导致的错误。
  • 提高代码可读性let的块级作用域使得代码结构更加清晰,易于理解,特别是在循环和条件语句中。
  • 更安全的作用域管理:使用let可以避免在不应该访问某个变量的作用域中误访问到它,从而提高代码的健壮性和安全性。
  • 避免全局污染:由于let声明的变量不会成为全局对象的属性,这有助于避免不必要的全局命名空间污染。

一条语句,多个变量

可以在一条语句中声明很多变量。该语句以 var 开头,并使用逗号分隔变量即可:var lastname="Doe", age=30, job="carpenter";

声明也可横跨多行:

var lastname="Doe",
age=30,
job="carpenter";

一条语句中声明的多个变量不可以同时赋同一个值:

var x,y,z=1;  // x,y 为 undefined, z 为 1。

Value = undefined

在计算机程序中,经常会声明无值的变量。未使用值来声明的变量,其值实际上是 undefined。

在执行过以下语句后,变量 carname 的值将是 undefined:var carname;

var 可以重新声明 变量

var 可以重新声明 JavaScript 变量,该变量的值不会丢失。

在以下两条语句执行后,变量 carname 的值依然是 "Volvo":

var carname="Volvo";
var carname;

变量提升

示例:

function fn(){
    console.log(name);  // name 先使用,后声明并初始化
    var name = '变量提升';
}
fn()
  • 在其他编程语言里,变量必须是先声明然后才能使用,不允许先使用后声明。
  • 但是在 js 里 "允许先使用后声明"。 因为在js执行的时候。它会首先检测你的代码. 发现在代码中会有 name使用,那么运行时就会变成如下的逻辑:
function fn(){
    var name;  // 直接把 name 的声明提到作用域的最前面,
    console.log(name);  // 这里使用name, 但是 name 只是声明,没有初始化,所以打印 undefined
    name = '变量提升';  // 这里进行初始化
}
fn()

这种把变量声明,提前到代码块作用域最前面运行的逻辑,被称为变量提升。

  • 在JavaScript中,变量提升(Hoisting)是指 "变量、函数" 的声明,在编译阶段被移动到它们所在作用域的顶部的行为。变量提升 只适用于通过var关键字声明的变量和函数声明,不适用于通过letconst声明的变量,以及函数表达式。JavaScript 严格模式(strict mode)不允许使用未声明的变量。
  • 变量提升 在其他语言里是绝对没有的,并且也不是什么好事情。在ES6中就明确了这样使用变量是不完善的,es6 提出使用 let 来声明变量,就不会出现该问题了。

JavaScript 只有声明的变量会提升,初始化的不会。示例:

function test_1() {
    /*只有声明的变量会提升*/
    var x1 = 10; // 初始化 x
    y1 = 20;
    console.log(x1 + "" + y1); // y 是先使用,后面声明
    var y1;          // 声明 变量 y
    console.log(x1 + "" + y1);
}

function test_2() {
    /*初始化的变量不会提升*/
    var x2 = 5; // 初始化 x
    console.log(x2 + "" + y2); // y 是先使用,后面声明和初始化
    var y2 = 7; // 初始化 y
}

test_1();
test_2();

函数声明的提升:

sayHello();

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

例子中的函数调用可以正常工作,并打印出"Hello!",因为函数声明被提升到了作用域的顶部。

总结:理解变量提升对于编写可预测和易于维护的JavaScript代码至关重要。推荐的做法是总是在作用域的顶部声明变量,以避免由于变量提升导致的潜在错误。此外,鉴于letconst提供的额外安全性及其遵循块级作用域的行为,推荐使用这两者而非var进行变量声明。

JavaScript 严格模式(use strict)

JavaScript 严格模式(strict mode)即在严格的条件下运行。

"use strict" 指令只允许出现在 脚本函数 的开头。

使用 "use strict" 指令

  • "use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。
  • "use strict" 的目的是指定代码在严格条件下执行。严格模式下你不能使用未声明的变量
  • 严格模式的声明:严格模式通过在脚本或函数的头部添加 use strict; 表达式来声明。

示例代码:

"use strict";
x = 3.14;       // 报错 (x 未定义)
"use strict";
myFunction();

function myFunction() {
    y = 3.14;   // 报错 (y 未定义)
}

在函数内部声明是局部作用域 (只在函数内使用严格模式)。示例:

x = 3.14;       // 不报错
myFunction();

function myFunction() {
   "use strict";
    y = 3.14;   // 报错 (y 未定义)
}

为什么使用严格模式:

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫。

"严格模式"体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。

另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解Javascript,让你变成一个更好的程序员。

严格模式的限制

1. 不允许使用未声明的变量。

"use strict";
x = 3.14;                // 报错 (x 未定义)

对象也是一个变量

"use strict";
x = {p1:10, p2:20};      // 报错 (x 未定义)

2. 不允许删除变量或对象。

"use strict";
var x = 3.14;
delete x;                // 报错

3. 不允许删除函数。

"use strict";
function x(p1, p2) {};
delete x;                // 报错 

4. 不允许变量重名:

"use strict";
function x(p1, p1) {};   // 报错

5. 不允许使用八进制:

"use strict";
var x = 010;             // 报错

6. 不允许使用转义字符:

"use strict";
var x = \010;            // 报错

7. 不允许对只读属性赋值:

"use strict";
var obj = {};
Object.defineProperty(obj, "x", {value:0, writable:false});

obj.x = 3.14;            // 报错

8. 不允许对一个使用 getter 方法读取的属性进行赋值

"use strict";
var obj = {get x() {return 0} };

obj.x = 3.14;            // 报错

9. 不允许删除一个不允许删除的属性:

"use strict";
delete Object.prototype; // 报错

10. 变量名不能使用 "eval" 字符串:

"use strict";
var eval = 3.14;         // 报错

11. 变量名不能使用 "arguments" 字符串:

"use strict";
var arguments = 3.14;    // 报错

12. 不允许使用以下这种语句:

"use strict";
with (Math){x = cos(2)}; // 报错

13. 由于一些安全原因,在作用域 eval() 创建的变量不能被调用:

"use strict";
eval ("var x = 2");
alert (x);               // 报错

14. 禁止this关键字指向全局对象。

function f(){
    return !this;
} 
// 返回false,因为"this"指向全局对象,"!this"就是false

function f(){ 
    "use strict";
    return !this;
} 
// 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。

因此,使用构造函数时,如果忘了加 new,this 不再指向全局对象,而是报错。

function f(){
    "use strict";
    this.a = 1;
};
f();// 报错,this未定义

15. 保留关键字

为了向将来Javascript的新版本过渡,严格模式新增了一些保留关键字:

  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static
  • yield
"use strict";
var public = 1500;      // 报错

JavaScript 数据类型

number 数字,不论是整数还是小数,数据类型都是 number
string 字符串
boolean 布尔值,只有两个,true 和 false,注意:都是小写开头
object 对象,这个比较特殊。可以理解为所有被new出来的东西都是对象 
undefined 表示未定义,所有没有被定义过的东西默认都是该类型 
null,None 表示 空值

值类型引用类型(即对象类型)

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
  • 引用数据类型:对象(Object)、数组(Array)、函数(Function)。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

null 和 Undefined 区别:

  • null 表示变量存在,值也存在,但是值为空。null 需要显式地赋给变量。使用typeof运算符检测null会返回"object",null在数字上下文中转换为0。null 通常由程序员显式设置,用来表示一个变量应该持有一个对象值,但目前尚未有值。
  • Undefined 表示变量存在,但是值不存在(就是没有进行初始化赋值)。当定义变量但不初始化时,默认值为undefined。使用typeof运算符检测undefined变量会返回"undefined"。undefined在数字上下文中转换为NaN。undefined通常是由JavaScript引擎设置,表示变量被声明了但未赋值。

var obj = null;
var x;
console.log(x); // undefined

在条件判断中,null 和 undefined 都会被转换为 false,但是在其他方面,它们有一些不同的行为。例如,对于对象属性的访问,如果该属性不存在,返回的是undefined,而不是null:

var obj = {a: 1};
console.log(obj.b); // undefined

另外,null 可以被赋值给任何类型的变量,而 undefined 只能被赋值给 undefined 类型的变量:

var x = null;
console.log(x); // null
 
var y;
y = undefined;
console.log(y); // undefined

null 的使用场景:

  • 初始化一个变量,表示该变量目前没有任何值。
  • 作为函数的参数,表示该函数的参数不需要传递任何值。
  • 作为对象的属性值,表示该属性目前没有任何值。
var x = null; // 初始化变量
function foo(param) {
  if (param === null) {
    console.log("param is null");
  }
}
foo(null); // 作为函数参数
var obj = {a: null}; // 作为对象属性值

undefined 的使用场景:

  • 变量声明但未初始化时,变量的默认值就是undefined。
  • 对象属性不存在时,访问该属性返回的值就是undefined。
  • 函数没有返回值时,返回值默认就是undefined。
var x; // 声明但未初始化
console.log(x); // undefined
var obj = {a: 1};
console.log(obj.b); // 访问不存在的属性
function foo() {
  // 没有返回值
}
console.log(foo()); // 函数没有返回值

null 和 undefined 都可以表示缺失或未定义的值,但是在具体使用时,需要根据场景选择使用哪种类型的值。

JavaScript 拥有动态类型

JavaScript 拥有动态类型。这意味着相同的变量可用作不同的类型,示例:

var x;               // x 为 undefined
var x = 5;           // 现在 x 为数字
var x = "John";      // 现在 x 为字符串

JavaScript 字符串

字符串是存储字符(比如 "Bill Gates")的变量。

字符串可以是引号中的任意文本。您可以使用单引号或双引号。实例:

var carname="Volvo XC60";
var carname='Volvo XC60';

您可以在字符串中使用引号,只要不匹配包围字符串的引号即可。实例:

var answer="It's alright";
var answer="He is called 'Johnny'";
var answer='He is called "Johnny"';

JavaScript 数字

JavaScript 只有一种数字类型。数字可以带小数点,也可以不带。实例:

var x1=34.00;      //使用小数点来写
var x2=34;         //不使用小数点来写

极大或极小的数字可以通过科学(指数)计数法来书写。实例:

var y=123e5;      // 12300000
var z=123e-5;     // 0.00123

JavaScript 布尔

布尔(逻辑)只能有两个值:true 或 false。布尔常用在条件测试中。

var x=true;
var y=false;

JavaScript 数组

下面的代码创建名为 cars 的数组:

var cars=new Array();
cars[0]="Saab";
cars[1]="Volvo";
cars[2]="BMW";

// 或者 (condensed array)
var cars=new Array("Saab","Volvo","BMW");

// 或者 (literal array):
var cars=["Saab","Volvo","BMW"];

数组下标是基于零的,所以第一个项目是 [0],第二个是 [1],以此类推。

Javasscript 中比较特殊,创建对象的时候可以不加小括号

示例:
var arr = new Array;
console.log(arr);
console.log(typeof arr);

数组的常用操作

arr.length;  // 数组长度

arr.push(data);  // 在数组后面添加数据

arr.pop();  // 删除数据, 从后面删除, 并返回被删除的内容

arr.shift()  // 删除数据, 从前面删除, 并返回被删除的内容

arr.unshift(xxx); // 在数组之前增加数据

arr.flat(); // 扁平化处理数组

// arr中的每一项循环出来. 分别去调用function函数, 会自动的将`数据`传递给函数的第一个参数

arr.forEach(function(e, i){  // 第二个参数是可选的

    console.log(i+"__"+e);

});

arr.join("连接符");  // 使用`连接符`将arr中的每一项拼接起来. 和python中的 "".join()雷同

字节 数组 ---> 字符串

在 JavaScript 中,可以使用多种方法将字节数组转换成字符串。

以下是几种常见的方法,推荐使用 TextDecoder

方法一:使用 TextDecoder(现代浏览器)

TextDecoder 是一个内置的对象,可以将字节数组转换为字符串。

const byteArray = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);

const decoder = new TextDecoder('utf-8');
const str = decoder.decode(byteArray);

console.log(str);  // 输出: "Hello World"

方法二:使用 String.fromCharCode(传统方法)

String.fromCharCode 可以将 Unicode 码点转换为字符串。通过使用 apply 方法,可以将整个字节数组一次性转换为字符串。

const byteArray = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100];

const str = String.fromCharCode.apply(null, byteArray);

console.log(str);  // 输出: "Hello World"

方法三:使用 map 和 String.fromCharCode

这是一种更现代化的方式,适用于较小的数组。

const byteArray = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100];

const str = byteArray.map(byte => String.fromCharCode(byte)).join('');

console.log(str);  // 输出: "Hello World"

方法四:使用 Buffer(Node.js 环境)

如果你在 Node.js 环境中,可以使用 Buffer 对象。

const byteArray = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100];

const buffer = Buffer.from(byteArray);
const str = buffer.toString('utf-8');

console.log(str);  // 输出: "Hello World"

方法五:使用 ArrayBuffer 和 DataView

这种方法适用于处理更复杂的字节数组和编码。

const byteArray = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);

let str = '';
for (let i = 0; i < byteArray.length; i++) {
    str += String.fromCharCode(byteArray[i]);
}

console.log(str);  // 输出: "Hello World"

字符串 ---> 字节 数组

方法一:使用 TextEncoder(现代浏览器)

TextEncoder 是一个内置的对象,可以将字符串转换为字节数组。

const str = "Hello World";

const encoder = new TextEncoder();
const byteArray = encoder.encode(str);

console.log(byteArray);  // 输出: Uint8Array(11) [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

方法二:手动转换(适用于 ASCII 字符)

可以通过遍历字符串,将每个字符的 Unicode 码点转换为字节。

const str = "Hello World";

const byteArray = [];
for (let i = 0; i < str.length; i++) {
    byteArray.push(str.charCodeAt(i));
}

console.log(byteArray);  // 输出: [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

方法三:使用 Buffer(Node.js 环境)

如果你在 Node.js 环境中,可以使用 Buffer 对象。

const str = "Hello World";

const buffer = Buffer.from(str, 'utf-8');
const byteArray = Array.from(buffer);

console.log(byteArray);  // 输出: [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

方法四:使用 ArrayBuffer 和 Uint8Array

这种方法适用于更复杂的字节数组操作。

const str = "Hello World";

const buffer = new ArrayBuffer(str.length);
const byteArray = new Uint8Array(buffer);
for (let i = 0; i < str.length; i++) {
    byteArray[i] = str.charCodeAt(i);
}

console.log(byteArray);  // 输出: Uint8Array(11) [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

方法五:使用 map 和 charCodeAt

这是一种更现代化的方式,适用于较小的字符串。

const str = "Hello World";

const byteArray = str.split('').map(char => char.charCodeAt(0));

console.log(byteArray);  // 输出: [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

JavaScript 对象

在JS中创建的对象 非常像 python 中的字典。对象由花括号括起来,在花括号内部,对象的属性以 "键值对 (name : value) " 的形式来定义,属性由逗号分隔。

不同的是:

  • JavaScript 中 对象的 key 可以用引号(单引号/双引号)括起来,也可以不用括。
  • Python 字典的key必须用引号括起来
var p = {
    name: "汪峰",
    age: 18,
    wife: "章子怡",
    
    chi: function(){
        console.log("吃饭")
    }
};

console.log(p.name);
console.log(p.age);
console.log(p['wife']);
p.chi()
p['chi']()

for(var n in p){
    if(typeof(p[n]) != 'function'){
        console.log(p[n])
    }
}

空格和折行无关紧要。声明可横跨多行:

var person={
    firstname : "John",
    lastname  : "Doe",
    id        :  5566
};

对象属性的两种寻址方式

name=person.lastname;
name=person["lastname"];

对象属性

JavaScript 对象:https://www.runoob.com/js/js-obj-intro.html

可以说 "JavaScript 中的对象是变量的容器"。( JavaScript的对象属性方法容器。 )

但是,我们通常认为 "JavaScript 对象是键值对的容器"。键值对通常写法为 name : value (键与值以冒号分割)。键值对在 JavaScript 对象通常称为 对象属性

对象键值对的写法类似于:

  • PHP 中的关联数组
  • Python 中的字典
  • C 语言中的哈希表
  • Java 中的哈希映射
  • Ruby 和 Perl 中的哈希表

示例:

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>

<p>创建和使用对象方法。</p>
<p>对象方法作为一个函数定义存储在对象属性中。</p>
<p id="demo"></p>
<script>
var person = {
    firstName: "John",
    lastName : "Doe",
    id : 5566,
    fullName : function() 
	{
       return this.firstName + " " + this.lastName;
    }
};
document.getElementById("demo").innerHTML = person.fullName();
</script>
	
</body>
</html>

name = person.fullName();   // 加上 () 表示调用函数。
name = person.fullName;     // 不加 () 表示属性,属性值 就是 函数体的代码

声明变量类型

当您声明新变量时,可以使用关键词 "new" 来声明其类型:

var carname=new String;
var x=      new Number;
var y=      new Boolean;
var cars=   new Array;
var person= new Object;

JavaScript 中的 值传递引用传递,共享传递

深入理解javascript按值传递与按引用传递:https://segmentfault.com/a/1190000012829900

  • 基本类型不可变类型,基本类型参数的传递与基本类型的复制一样,传递的是变量值。场景:JavaScript 中如果传递给函数的形参是不可变类型,则在函数中改变形参的值不会影响外部实参的值。
  • 对象类型可变类型,引用类型参数的传递与引用类型的复制一样,传递的是内存地址。场景:JavaScript 中如果传递给形参是对象类型,由于对象是可变(mutable),当修改形参对象的属性值,也会影响到实参的属性值。
function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);   // 10
console.log(obj1.item);    // changed
console.log(obj2.item);    // unchanged

值传递

传值的意思就是:传内存拷贝。

函数内的num, obj1, obj2都将是一份新的内存,与调用函数之前定义的三个变量毫无关系。函数内无论怎么修改这三个参数,外部定义的三个变量的值始终不变

引用传递

传引用的意思就是:传内存指针

函数内的num, obj1, obj2都分别指向一块内存,该内存就是调用函数之前定义的三个变量时创建的内存。函数内对这三个参数所做的任何改动,都将反映到外部定义的三个变量上。

  • 基本类型的传递如同基本类型的复制一样,
  • 引用类型值的传递,如同引用类型变量的复制一样。

总结:javascript 函数参数都是按值传递(都是栈内数据的拷贝)。基本类型传的是值本身(因为直接把值存在栈内),引用类型传的是对象在内存里面的地址 (因为复杂对象存在堆内,所以在栈里存对象所在的堆地址)。

共享传递

JS是按值传递还是按引用传递? 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略(Evaluation Strategy)。它决定变量之间、函数调用时实参和形参之间值是如何传递的。

按值传递 VS. 按引用传递

  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。按值传递由于每次都需要克隆副本,对一些复杂类型,性能较低。
  • 按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG。

两种传值方式都有各自的问题。我们先看一个C的例子来了解按值和引用传递的区别: 

void Modify(int p, int * q)
{
    p = 27; // 按值传递 - p是实参a的副本, 只有p被修改
    *q = 27; // q是b的引用,q和b都被修改
}
int main()
{
    int a = 1;
    int b = 1;
    Modify(a, &b);   // a 按值传递, b 按引用传递,
                     // a 未变化, b 改变了
    return(0);
}

这里我们可以看到:

a => p按值传递时,修改形参p的值并不影响实参a,p只是a的副本。
b => q是按引用传递,修改形参q的值时也影响到了实参b的值。

探究JS值的传递方式

JS的基本类型,是按值传递的。

var a = 1;
function foo(x) {
    x = 2;
}
foo(a);
console.log(a); // 仍为1, 未受x = 2赋值所影响

再来看对象:

var obj = {x : 1};
function foo(o) {
    o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

说明 o 和 obj 是同一个对象,o 不是 obj 的副本。所以不是按值传递。 但这样是否说明 JS 的对象是按引用传递的呢?我们再看下面的例子:

var obj = {x : 1};
function foo(o) {
    o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

如果是按引用传递,修改形参o的值,应该影响到实参才对。但这里修改o的值并未影响obj。 因此JS中的对象并不是按引用传递。那么究竟对象的值在JS中如何传递的呢?

按共享传递 call by sharing

准确的说,JS中的基本类型按值传递,对象类型按共享传递的(call by sharing,也叫按对象传递、按对象共享传递)。最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。

该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值。

var obj = {x : 1};
function foo(o) {
    o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

然而,虽然引用是副本,引用的对象是相同的。它们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值。

var obj = {x : 1};
function foo(o) {
    o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

对于对象类型,由于对象是可变(mutable)的,修改对象本身会影响到共享这个对象的引用和引用副本。而对于基本类型,由于它们都是不可变的(immutable),按共享传递与按值传递(call by value)没有任何区别,所以说JS基本类型既符合按值传递,也符合按共享传递。
 
var a = 1; // 1是number类型,不可变 var b = a; b = 6;
据按共享传递的求值策略,a和b是两个不同的引用(b是a的引用副本),但引用相同的值。由于这里的基本类型数字1不可变,所以这里说按值传递、按共享传递没有任何区别。
 
基本类型的不可变(immutable)性质
基本类型是不可变的(immutable),只有对象是可变的(mutable). 例如数字值100, 布尔值true, false,修改这些值(例如把1变成3, 把true变成100)并没有什么意义。比较容易误解的,是JS中的string。有时我们会尝试“改变”字符串的内容,但在JS中,任何看似对string值的”修改”操作,实际都是创建新的string值。

var str = "abc";
str[0]; // "a"
str[0] = "d";
str; // 仍然是"abc";赋值是无效的。没有任何办法修改字符串的内容

而对象就不一样了,对象是可变的。

var obj = {x : 1};
obj.x = 100;
var o = obj;
o.x = 1;
obj.x; // 1, 被修改
o = true;
obj.x; // 1, 不会因o = true改变

这里定义变量obj,值是object,然后设置obj.x属性的值为100。而后定义另一个变量o,值仍然是这个object对象,此时obj和o两个变量的值指向同一个对象(共享同一个对象的引用)。所以修改对象的内容,对obj和o都有影响。但对象并非按引用传递,通过o = true修改了o的值,不会影响obj。

JavaScript 类型转换

Number() 转换为数字, String() 转换为字符串, Boolean() 转换为布尔值。

JavaScript 数据类型

在 JavaScript 中有 6 种不同的数据类型:

  • string
  • number
  • boolean
  • object
  • function
  • symbol

3 种对象类型:

  • Object
  • Date
  • Array

2 个不包含任何值的数据类型:

  • null
  • undefined

typeof 操作符

你可以使用 typeof 操作符来查看 JavaScript 变量的数据类型。

console.log(typeof "John")                 // 返回 string
console.log(typeof 3.14)                   // 返回 number
console.log(typeof NaN)                    // 返回 number
console.log(typeof false)                  // 返回 boolean
console.log(typeof [1,2,3,4])              // 返回 object
console.log(typeof {name:'John', age:34})  // 返回 object
console.log(typeof new Date())             // 返回 object
console.log(typeof function () {})         // 返回 function
console.log(typeof myCar)                  // 返回 undefined (如果 myCar 没有声明)
console.log(typeof null)                   // 返回 object
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<p> typeof 操作符返回变量、对象、函数、表达式的类型。</p>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = 
    typeof "john" + "<br>" +
    typeof 3.14 + "<br>" +
    typeof NaN + "<br>" +
    typeof false + "<br>" +
    typeof [1,2,3,4] + "<br>" +
    typeof {name:'john', age:34} + "<br>" +
    typeof new Date() + "<br>" +
    typeof function () {} + "<br>" +
    typeof myCar + "<br>" +
    typeof null;
</script>

</body>
</html>

注意:

  • NaN 的数据类型是 number
  • 数组(Array)的数据类型是 object
  • 日期(Date)的数据类型为 object
  • null 的数据类型是 object
  • 未定义变量的数据类型为 undefined

如果对象是 JavaScript Array 或 JavaScript Date ,我们就无法通过 typeof 来判断他们的类型,因为都是 返回 object。

constructor 属性

constructor 属性返回所有 JavaScript 变量的 构造函数

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<p> constructor 属性返回变量或对象的构造函数。</p>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = 
    "john".constructor + "<br>" +
    (3.14).constructor + "<br>" +
    false.constructor + "<br>" +
    [1,2,3,4].constructor + "<br>" +
    {name:'john', age:34}.constructor + "<br>" +
    new Date().constructor + "<br>" +
    function () {}.constructor;
</script>

</body>
</html>

运行结果:

constructor 属性返回变量或对象的构造函数。

function String() { [native code] }
function Number() { [native code] }
function Boolean() { [native code] }
function Array() { [native code] }
function Object() { [native code] }
function Date() { [native code] }
function Function() { [native code] }

nodejs 执行结果:

console.log("John".constructor);                 // [Function: String]
console.log((3.14).constructor);                 // [Function: Number]
console.log(false.constructor);                  // [Function: Boolean]
console.log([1,2,3,4].constructor);              // [Function: Array]
console.log({name:'John', age:34}.constructor);  // [Function: Object]
console.log(new Date().constructor)              // [Function: Date]
console.log(function () {}.constructor)          // [Function: Function]

可以使用 constructor 属性来查看对象是否为数组 (包含字符串 "Array")。示例:判断是否是 array

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

var str = '';
console.log(isArray(str));

使用 constructor 属性来查看对象是否为日期 (包含字符串 "Date"):

function isDate(myDate) {
    return myDate.constructor.toString().indexOf("Date") > -1;
}

JavaScript 类型转换

JavaScript 变量可以转换为 新变量其他数据类型

  • 通过使用 JavaScript 函数
  • 通过 JavaScript 自身自动转换

数字 转换为 字符串

全局方法 String() 可以将数字转换为字符串。该方法可用于任何类型的数字,字母,变量,表达式:

String(x)         // 将变量 x 转换为字符串并返回
String(123)       // 将数字 123 转换为字符串并返回
String(100 + 23)  // 将数字表达式转换为字符串并返回

Number 方法 toString() 也是有同样的效果。

var x = 12345;
console.log(x.toString());
console.log((123).toString());
console.log((10000 + 23).toString());

在 Number 方法 章节中,你可以找到更多数字转换为字符串的方法:

方法 描述
toExponential() 把对象的值转换为指数计数法。
toFixed() 把数字转换为字符串,结果的小数点后有指定位数的数字。
toPrecision() 把数字格式化为指定的长度。

布尔值 转换为 字符串

//全局方法 String() 可以将布尔值转换为字符串。
String(false) // 返回 "false"
String(true) // 返回 "true"

// Boolean 方法toString()也有相同的效果。
false.toString()// 返回 "false"
true.toString() // 返回 "true"

将日期转换为字符串

// Date() 返回字符串。
Date() // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

// 全局方法 String() 可以将日期对象转换为字符串。
String(new Date())  // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

// Date 方法toString()也有相同的效果。
obj = new Date()
obj.toString() // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

在 Date 方法 章节中,你可以查看更多关于日期转换为字符串的函数:

方法 描述
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。

字符串 转换为 数字

全局方法 Number() 可以将字符串转换为数字。
字符串包含数字(如 "3.14") 转换为数字 (如 3.14)。
空字符串转换为 0。
其他的字符串会转换为 NaN (不是个数字)。

Number("3.14")  // 返回 3.14
Number(" ")     // 返回 0
Number("")      // 返回 0
Number("99 88") // 返回 NaN

在 Number 方法 章节中,你可以查看到更多关于字符串转为数字的方法:

方法 描述
parseFloat() 解析一个字符串,并返回一个浮点数。
parseInt() 解析一个字符串,并返回一个整数。

一元运算符 +

Operator + 可用于将变量转换为数字。实例:

var y = "5";  // y 是一个字符串
var x = +y; // x 是一个数字
console.log(x)

如果变量不能转换,它仍然会是一个数字,但值为 NaN (不是一个数字)。实例:

var y = "John";   // y 是一个字符串
var x = + y;      // x 是一个数字 (NaN)
console.log(typeof x)
console.log(x)

将布尔值转换为数字

全局方法 Number() 可将布尔值转换为数字。

Number(false)     // 返回 0
Number(true)      // 返回 1

将日期转换为数字

// 全局方法 Number() 可将日期转换为数字。
d = new Date();
console.log(Number(d))  // 返回 时间戳

// 日期方法 getTime() 也有相同的效果。
d = new Date();
d.getTime()        // 返回 时间戳

自动转换类型

当 JavaScript 尝试操作一个 "错误" 的数据类型时,会自动转换为 "正确" 的数据类型。

以下输出结果不是你所期望的:

5 + null    // 返回 5         null 转换为 0
"5" + null  // 返回"5null"    null 转换为 "null"
"5" + 1     // 返回 "51"      1 转换为 "1" 
"5" - 1     // 返回 4         "5" 转换为 5

自动转换为字符串

当尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

// 当你尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

document.getElementById("demo").innerHTML = myVar;
myVar = {name:"Fjohn"}  // toString 转换为 "[object Object]"
myVar = [1,2,3,4]       // toString 转换为 "1,2,3,4"
myVar = new Date()      // toString 转换为 "Fri Jul 18 2014 09:08:55 GMT+0200"

// 数字和布尔值也经常相互转换:
myVar = 123             // toString 转换为 "123"
myVar = true            // toString 转换为 "true"
myVar = false           // toString 转换为 "false"

下表展示了使用不同的数值转换为数字(Number), 字符串(String), 布尔值(Boolean):

原始值 转换为数字 转换为字符串 转换为布尔值 实例
false 0 "false" false 尝试一下 »
true 1 "true" true 尝试一下 »
0 0 "0" false 尝试一下 »
1 1 "1" true 尝试一下 »
"0" 0 "0" true 尝试一下 »
"000" 0 "000" true 尝试一下 »
"1" 1 "1" true 尝试一下 »
NaN NaN "NaN" false 尝试一下 »
Infinity Infinity "Infinity" true 尝试一下 »
-Infinity -Infinity "-Infinity" true 尝试一下 »
"" 0 "" false 尝试一下 »
"20" 20 "20" true 尝试一下 »
"Runoob" NaN "Runoob" true 尝试一下 »
[ ] 0 "" true 尝试一下 »
[20] 20 "20" true 尝试一下 »
[10,20] NaN "10,20" true 尝试一下 »
["Runoob"] NaN "Runoob" true 尝试一下 »
["Runoob","Google"] NaN "Runoob,Google" true 尝试一下 »
function(){} NaN "function(){}" true 尝试一下 »
{ } NaN "[object Object]" true 尝试一下 »
null 0 "null" false 尝试一下 »
undefined NaN "undefined" false 尝试一下 »

let、const、全局变量局部变量

总结:

  • 使用var关键字声明的全局作用域变量属于window对象。
  • 使用let关键字声明的全局作用域变量不属于window对象。
  • 使用var关键字声明的变量在任何地方都可以修改。
  • 在相同的作用域或块级作用域中,不能使用let关键字来重置var关键字声明的变量。
  • 在相同的作用域或块级作用域中,不能使用let关键字来重置let关键字声明的变量。
  • let关键字在不同作用域,或不用块级作用域中是可以重新声明赋值的。
  • 在相同的作用域或块级作用域中,不能使用const关键字来重置var和let关键字声明的变量。
  • 在相同的作用域或块级作用域中,不能使用const关键字来重置const关键字声明的变量
  • const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
  • var 关键字定义的变量可以先使用后声明。
  • let 关键字定义的变量需要先声明再使用。
  • const 关键字定义的常量,声明时必须进行初始化,且初始化后不可再修改。

ECMAScript 2015(ECMAScript 6)

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const

  • let 声明的变量只在 let 命令所在的代码块内有效。
  • const 声明一个只读的常量,一旦声明,常量的值就不能改变。
  • 在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量

理解JavaScript中的作用域和上下文
关于词法作用域的小疑惑:        
JavaScript 预编译与作用域:     
深入理解js作用域:              
js深入理解] 作用域一系列问题:  
JavaScript进阶-执行上下文栈和变量对象:https://juejin.cn/post/6844904033308655623

全局变量

在函数外使用 var 和 let 声明的变量作用域是全局的,即为全局变量。全局变量在 JavaScript 程序的任何地方都可以访问。因它们的作用域都是全局的。

var carName = "Volvo";
 
// 这里可以使用 carName 变量
 
function myFunction() {
    // 这里也可以使用 carName 变量
}

未声明的变量 赋值

没有使用 var 或者 let 声明的变量,则全部挂载到全局变量 global (node 中是 global,网页中是 window) 上作为一个属性。使用时 global 或者 window 可以省略,也相当于全局变量。

// 此处可调用 carName 变量
function myFunction() {
    // 没有使用 var或者let声明变量, 所以 carName 挂载到 global 上
    carName = "Volvo";
    console.log(carName)
}
myFunction();
console.log(carName);
console.log(global.carName);
console.log(global.carName === carName);
v0 = 123;  // 注意: 这里没有使用 var或者let。所以 v0挂在到 global 上
global.v1 = 'bobo';
global.v2 = 'jay';
global.v3 = {
    'age': 30
};
//定义了三个全局变量
console.log(v1, v2, v3.age);
console.log(global);

非严格模式下给未声明变量赋值创建的全局变量,是全局对象的可配置属性,可以删除。

var var1 = 1;  // 不可配置全局属性
var2 = 2;      // 没有使用 var 声明,可配置全局属性

console.log(this.var1);   // 1
console.log(window.var1); // 1
console.log(window.var2); // 2

delete var1;        // false 无法删除
console.log(var1);  //1

delete var2; 
console.log(delete var2); // true
console.log(var2);        // 已经删除 报错变量未定义

局部变量

在函数内声明的变量只能函数内部访问,作用域是函数内部。这种变量就叫局部变量

局部变量:只能在函数内部访问。示例:

function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}
// 此处不能调用 carName 变量
console.log(carName)

因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。

局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。

块级 作用域 (Block Scope)

使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。

{ 
    var x = 2; 
}
// 这里可以使用 x 变量

在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

{ 
    let x = 2;
}
// 这里不能使用 x 变量

"重新声明、重置" 变量

使用 var 关键字声明的变量在任何地方都可以修改。

var x = 2;  // x 为 2
var x = 3;  // 现在 x 为 3

示例:在块中使用 var 重新声明变量时,也会改变块外的变量:

var x = 10;
// 这里输出 x 为 10
{ 
    var x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 2

let 关键字就可以解决这个问题,因为它只在 let 命令所在的代码块 {} 内有效。

var x = 10;
// 这里输出 x 为 10
{ 
    let x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 var 关键字声明的变量:

var x = 2;       // 合法
let x = 3;       // 不合法

{
    var x = 4;   // 合法
    let x = 5   // 不合法
}

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
let x = 3;       // 不合法

{
    let x = 4;   // 合法
    let x = 5;   // 不合法
}

在相同的作用域或块级作用域中,不能使用 var 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
var x = 3;       // 不合法

{
    let x = 4;   // 合法
    var x = 5;   // 不合法
}

let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

let x = 2;       // 合法

{
    let x = 3;   // 合法
}

{
    let x = 4;   // 合法
}

循环 作用域

使用 var 关键字,它声明的变量是全局的,包括循环体内与循环体外。示例:

var i = 5;
for (var i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 10

使用 let 关键字, 它声明的变量作用域只在循环体内,循环体外的变量不受影响。

let i = 5;
for (let i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 5

HTML 中使用全局变量

  • 在 JavaScript 中, 全局作用域是针对 JavaScript 环境。
  • 在 HTML 中, 全局作用域是针对 window 对象。使用 var 关键字声明的全局作用域变量属于 window 对象:

示例:( 示例:尝试一下

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>

<h2>JavaScript 全局变量</h2>

<p>使用 var 关键字声明的全局作用域变量属于 window 对象。</p>

<p id="demo"></p>

<script>
var carName = "Volvo";

// 可以使用 window.carName 访问变量
document.getElementById("demo").innerHTML = "I can display " + window.carName;
</script>

</body>
</html>

使用 let 关键字声明的全局作用域变量不属于 window 对象。示例:尝试一下

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>

<h2>JavaScript 全局变量</h2>

<p>使用 let 关键字声明的全局作用域变量不属于 window 对象。</p>

<p id="demo"></p>

<script>
let carName = "Volvo";

// 不能使用 window.carName 访问变量
document.getElementById("demo").innerHTML = "I can not display " + window.carName;
</script>

</body>
</html>

变量 生命周期

  • JavaScript 变量的生命期从它们被声明的时间开始。
  • 局部变量在函数执行完毕后销毁。函数参数只在函数内起作用,是局部变量。
  • 全局变量在页面关闭后销毁。在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。

注意:你的全局变量,或者函数,可以覆盖 window 对象的变量或者函数。局部变量,包括 window 对象可以覆盖全局变量和函数。

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>

<p>
在 HTML 中, 所有全局变量都会成为 window 变量。
</p>
<p id="demo"></p>
<script>
myFunction();
document.getElementById("demo").innerHTML =
	"我可以显示 " + window.carName;
function myFunction() 
{
    carName = "Volvo";
}
</script>

</body>
</html>

const 关键字

const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改。
在相同的作用域或块级作用域中,不能使用const关键字来重置var、let、const声明的变量。

const PI = 3.141592653589793;
PI = 3.14;      // 报错
PI = PI + 10;   // 报错

const 定义常量与使用 let  定义的变量相似:

  • 二者都是块级作用域
  • 都不能和它所在作用域内的其他变量或函数拥有相同的名称

两者还有以下两点区别:

  • const声明的常量必须初始化,而let声明的变量不用
  • const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
var x = 10;
// 这里输出 x 为 10
{ 
    const x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

const 声明的常量必须初始化:

// 错误写法
const PI;
PI = 3.14159265359;

// 正确写法
const PI = 3.14159265359;

const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

const x = 2;       // 合法

{
    const x = 3;   // 合法
}

{
    const x = 4;   // 合法
}

const 修饰 "数组、对象"

可以看理解为指针,指向的地址不能变,但是地址里面的内容是可以变化的

const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。使用 const 定义的对象或者数组,其实是可变的。

下面的代码并不会报错:

// 创建常量对象
const car = {type:"Fiat", model:"500", color:"white"};
 
// 修改属性:
car.color = "red";
 
// 添加属性
car.owner = "Johnson";

但是我们不能对常量对象重新赋值:

const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"};    // 错误

以下实例修改常量数组:

// 创建常量数组
const cars = ["Saab", "Volvo", "BMW"];
 
// 修改元素
cars[0] = "Toyota";
 
// 添加元素
cars.push("Audi");

但是我们不能对常量数组重新赋值:

const cars = ["Saab", "Volvo", "BMW"];
cars = ["Toyota", "Volvo", "Audi"];    // 错误

JavaScript 函数

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

function functionname()
{
    // 执行代码
}

当调用该函数时,会执行函数内的代码。

在调用函数时,可以向函数传递值,这些值被称为参数。这些参数可以在函数中使用。可以发送任意多的参数,由逗号 (,) 分隔:

myFunction(argument1,argument2)

当声明函数时,请把参数作为变量来声明:

function myFunction(var1,var2)
{
    // 代码
}

变量和参数按顺序对应。第一个变量就是第一个被传递的参数的给定的值,以此类推。实例:

<!DOCTYPE html>
<html>	
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head>
<body>

<p>点击这个按钮,来调用带参数的函数。</p>
<button onclick="myFunction('Harry Potter','Wizard')">点击这里</button>
<script>
function myFunction(name,job){
	alert("Welcome " + name + ", the " + job);
}
</script>

</body>
</html>

上面的函数在按钮被点击时会提示 "Welcome Harry Potter, the Wizard"。

JavaScript 代码规范

规范的代码可以更易于阅读与维护。代码规范一般在开发前规定,可以跟你的团队成员来协商设置。

代码规范通常包括以下几个方面:

  • 变量函数 的命名规则
  • 空格,缩进,注释 的使用规则。
  • 其他常用规范……

变量名。变量名推荐使用驼峰法来命名(camelCase):

firstName = "John";
lastName = "Doe";
price = 19.90;
tax = 0.20;
fullPrice = price + (price * tax);

空格与运算符。通常运算符 ( = + - * / ) 前后需要添加空格。实例:

var x = y + z;
var values = ["Volvo", "Saab", "Fiat"];

代码缩进。通常使用 4 个空格符号来缩进代码块。不推荐使用 TAB 键来缩进,因为不同编辑器 TAB 键的解析不一样。函数缩进

function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}

语句规则。简单语句的通用规则:一条语句通常以分号作为结束符。实例:

varvalues = ["Volvo", "Saab", "Fiat"];

varperson = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

复杂语句的通用规则:

  • 将左花括号放在第一行的结尾。
  • 左花括号前添加一空格。
  • 将右花括号独立放在一行。
  • 不要以分号结束一个复杂的声明。

函数:

function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}

toCelsius()

var x=0, y;
for (var i = 0; i < 5; i++) {
    x += i;
    console.log(x.toString())
    console.log(y)
}

var time = 10
if (time < 20) {
    greeting = "Good day";
} else {
    greeting = "Good evening";
}
console.log(greeting)

对象定义的规则:

  • 将左花括号与类名放在同一行。
  • 冒号与属性值间有个空格。
  • 字符串使用双引号,数字不需要。
  • 最后一个属性-值对后面不要添加逗号。
  • 将右花括号独立放在一行,并以分号作为结束符号。

实例:

var person = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

短的对象代码可以直接写成一行。实例:

var person = {firstName: "John", lastName: "Doe", age: 50, eyeColor: "blue"};

命名规则

一般很多代码语言的命名规则都是类似的,例如:

  • 变量和函数为小驼峰法标识, 即除第一个单词之外,其他单词首字母大写( lowerCamelCase
  • 全局变量为大写 (UPPERCASE )
  • 常量 (如 PI) 为大写 (UPPERCASE )

变量命名你是否使用这几种规则: hyp-henscamelCase, 或 under_scores ?

HTML 和 CSS 的横杠(-)字符:

HTML5 属性可以以 data- (如:data-quantity, data-price) 作为前缀。

CSS 使用 - 来连接属性名 (font-size)。

- 通常在 JavaScript 中被认为是减法,所以不允许使用。

变量名不要以 $ 作为开始标记,会与很多 JavaScript 库冲突。

下划线:

  • 很多程序员比较喜欢使用下划线(如:date_of_birth), 特别是在 SQL 数据库中。PHP 语言通常都使用下划线。
  • 帕斯卡拼写法(PascalCase):帕斯卡拼写法(PascalCase) 在 C 语言中语言较多。
  • 驼峰法:JavaScript 中通常推荐使用驼峰法,jQuery 及其他 JavaScript 库都使用驼峰法。

HTML 载入外部 JavaScript 文件

使用简洁的格式载入 JavaScript 文件 ( type 属性不是必须的):<script src="myscript.js">

使用 JavaScript 访问 HTML 元素

一个糟糕的 HTML 格式可能会导致 JavaScript 执行错误。以下两个 JavaScript 语句会输出不同结果。实例:

var obj = getElementById("Demo")
var obj = getElementById("demo")
尝试一下 »

HTML 与 JavaScript 尽量使用相同的命名规则。访问 HTML(5) 代码规范

文件扩展名

  • HTML 文件后缀可以是 .html (或 .htm)。
  • CSS 文件后缀是 .css 。
  • JavaScript 文件后缀是 .js 。

使用小写文件名

  • 大多 Web 服务器 (Apache, Unix) 对大小写敏感: london.jpg 不能通过 London.jpg 访问。
  • 其他 Web 服务器 (Microsoft, IIS) 对大小写不敏感: london.jpg 可以通过 London.jpg 或 london.jpg 访问。
  • 你必须保持统一的风格,建议统一使用小写的文件名。

JavaScript 事件

HTML 事件是发生在 HTML 元素上的事情。当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件。

HTML 事件可以是浏览器行为,也可以是用户行为。以下是 HTML 事件的实例:

  • HTML 页面完成加载
  • HTML input 字段改变时
  • HTML 按钮被点击

通常,当事件发生时,你可以做些事情。在事件触发时 JavaScript 可以执行一些代码。

HTML 元素中可以添加事件属性,使用 JavaScript 代码来添加 HTML 元素。

  • 单引号:<some-HTML-element some-event='JavaScript 代码'>
  • 双引号:<some-HTML-element some-event="JavaScript 代码">

按钮元素中添加了 onclick 属性:<button οnclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>

代码将修改自身元素的内容 (使用 this.innerHTML):<button οnclick="this.innerHTML=Date()">现在的时间是?</button>

常见的 HTML 事件

下面是一些常见的HTML事件的列表:

事件 描述
onchange HTML 元素改变
onclick 用户点击 HTML 元素
onmouseover 用户在一个HTML元素上移动鼠标
onmouseout 用户从一个HTML元素上移开鼠标
onkeydown 用户按下键盘按键
onload 浏览器已完成页面的加载

更多事件列表: JavaScript 参考手册 - HTML DOM 事件

JavaScript 可以做什么?

事件可以用于处理表单验证,用户输入,用户行为及浏览器动作:

  • 页面加载时触发事件
  • 页面关闭时触发事件
  • 用户点击按钮执行动作
  • 验证用户输入内容的合法性
  • 等等 ...

可以使用多种方法来执行 JavaScript 事件代码:

  • HTML 事件属性可以直接执行 JavaScript 代码
  • HTML 事件属性可以调用 JavaScript 函数
  • 你可以为 HTML 元素指定自己的事件处理程序
  • 你可以阻止事件的发生。
  • 等等 ...

在 HTML DOM 章节中你将会学到更多关于事件及事件处理程序的知识。

JavaScript 字符串

字符串用于存储和处理文本。

  • 字符串可以是插入到引号中的任何字符。可以使用 单引号双引号。
  • 字符串的索引从 0 开始,这意味着第一个字符索引值为 [0],第二个为 [1], 以此类推。
var carname = "Volvo XC60";
var carname = 'Volvo XC60';

// 可以使用索引位置来访问字符串中的每个字符。
var character = carname[7];

可以在字符串中使用引号,字符串中的引号不要与字符串的引号相同,实例:

var answer = "It's alright";
var answer = "He is called 'Johnny'";
var answer = 'He is called "Johnny"';

你也可以在字符串添加转义字符来使用引号。实例:

var x = 'It\'s alright';
var y = "He is called \"Johnny\"";

字符串长度

可以使用内置属性 length 来计算字符串的长度:

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var txt_len = txt.length;
console.log('txt_len = ' + txt_len)
for (var i = 0; i < txt_len; i++) {
    console.log(txt[i])
}

特殊字符

在 JavaScript 中,字符串写在单引号或双引号中。因为这样,以下实例 JavaScript 无法解析: "We are the so-called "Vikings" from the north."

字符串 "We are the so-called " 被截断。

如何解决以上的问题呢?可以使用反斜杠 (\) 来转义 "Vikings" 字符串中的双引号,如下: "We are the so-called \"Vikings\" from the north."

 反斜杠是一个转义字符。 转义字符将特殊字符转换为字符串字符。转义字符 (\) 可以用于转义撇号,换行,引号,等其他特殊字符。

下表中列举了在字符串中可以使用转义字符转义的特殊字符:

代码 输出
\' 单引号
\" 双引号
\\ 反斜杠
\n 换行
\r 回车
\t tab(制表符)
\b 退格符
\f 换页符

字符串可以是对象

  • 通常, JavaScript 字符串是原始值,可以使用字符创建: var firstName = "John"
  • 但我们也可以使用 new 关键字将字符串定义为一个对象: var firstName = new String("John")
var x = "John";
var y = new String("John");
typeof x  // 返回 String
typeof y  // 返回 Object

注意:不要创建 String 对象。它会拖慢执行速度,并可能产生其他副作用。( === 为绝对相等,即数据类型与值都必须相等。 )

var x = "John";
var y = new String("John");
var bval = (x === y)   // 结果为 false,因为 x 是字符串,y 是对象
console.log(bval)      // 输出结果 false

字符串属性和方法

原始值字符串,如 "John", 没有属性和方法(因为他们不是对象)。

原始值可以使用 JavaScript 的属性和方法,因为 JavaScript 在执行方法和属性时可以把原始值当作对象。

字符串方法我们将在下一章节中介绍。

字符串属性

属性 描述
constructor 返回创建字符串属性的函数
length 返回字符串的长度
prototype 允许您向对象添加属性和方法

字符串方法

更多方法实例可以参见:JavaScript String 对象

方法 描述
charAt() 返回指定索引位置的字符
charCodeAt() 返回指定索引位置字符的 Unicode 值
concat() 连接两个或多个字符串,返回连接后的字符串
fromCharCode() 将 Unicode 转换为字符串
indexOf() 返回字符串中检索指定字符第一次出现的位置
lastIndexOf() 返回字符串中检索指定字符最后一次出现的位置
localeCompare() 用本地特定的顺序来比较两个字符串
match() 找到一个或多个正则表达式的匹配
replace() 替换与正则表达式匹配的子串
search() 检索与正则表达式相匹配的值
slice() 提取字符串的片断,并在新的字符串中返回被提取的部分
split() 把字符串分割为子字符串数组
substr() 从起始索引号提取字符串中指定数目的字符
substring() 提取字符串中两个指定的索引号之间的字符
toLocaleLowerCase() 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLocaleUpperCase() 根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLowerCase() 把字符串转换为小写
toString() 返回字符串对象值
toUpperCase() 把字符串转换为大写
trim() 移除字符串首尾空白
valueOf() 返回某个字符串对象的原始值

JavaScript 运算符

运算符优先级运算符优先级 - JavaScript | MDN

运算符 = 用于赋值。运算符 = 用于给 JavaScript 变量赋值。

运算符 + 用于加值。算术运算符 + 用于把值加起来。

实例:指定变量值,并将值相加:

y = 5;
z = 2;
x = y + z; //执行后,x 的值是 7

JavaScript 算术运算符

运算符 描述 例子 x 运算结果 y 运算结果 在线实例
+ 加法 x=y+2 7 5 实例 »
- 减法 x=y-2 3 5 实例 »
* 乘法 x=y*2 10 5 实例 »
/ 除法 x=y/2 2.5 5 实例 »
% 取模(余数) x=y%2 1 5 实例 »
++ 自增 x=++y 6 6 实例 »
x=y++ 5 6 实例 »
-- 自减 x=--y 4 4 实例 »
x=y-- 5 4 实例 »

JavaScript 赋值运算符

赋值运算符用于给 JavaScript 变量赋值。赋值运算符:

运算符 例子 等同于 运算结果 在线实例
= x=y x=5 实例 »
+= x+=y x=x+y x=15 实例 »
-= x-=y x=x-y x=5 实例 »
*= x*=y x=x*y x=50 实例 »
/= x/=y x=x/y x=2 实例 »
%= x%=y x=x%y x=0 实例 »

用于字符串的 + 运算符

+ 运算符用于把文本值或字符串变量加起来(连接起来)。如需把两个或多个字符串变量连接起来,请使用 + 运算符。

实例:如需把两个或多个字符串变量连接起来,请使用 + 运算符:

txt1 = "What a very";
txt2 = "nice day";
txt3 = txt1 + txt2; // txt3 值为 What a verynice day

字符串数字进行加法运算

规则:如果把数字与字符串相加,结果将成为字符串!

两个数字相加,返回数字相加的和,如果数字与字符串相加,返回字符串,如下实例:

x = 5 + 5;        // x 值为 10
y = "5" + 5;      // y 值为 55
z = "Hello" + 5;  // z 值为 Hello5
a = 123 + 'hello' // a 值为 123hello

JavaScript 比较 和 逻辑 运算符

比较和逻辑运算符用于测试 true 或者 false

比较运算符

比较运算符在逻辑语句中使用,以测定变量或值是否相等。比较运算符:

运算符 描述 比较 返回值 实例
== 等于 x==8 false 实例 »
x==5 true 实例 »
=== 绝对等于(值和类型均相等) x==="5" false 实例 »
x===5 true 实例 »
!=  不等于 x!=8 true 实例 »
!==  不绝对等于(值和类型有一个不相等,或两个都不相等) x!=="5" true 实例 »
x!==5 false 实例 »
>  大于 x>8 false 实例 »
<  小于 x<8 true 实例 »
>=  大于或等于 x>=8 false 实例 »
<=  小于或等于 x<=8 true 实例 »

逻辑运算符

逻辑运算符用于测定变量或值之间的逻辑。逻辑运算符:

运算符 描述 例子
&& and (x < 10 && y > 1) 为 true
|| or (x==5 || y==5) 为 false
! not !(x==y) 为 true

注意:

  • &    按 位 与
  • |     按 位 或  
function a(){console.log('a');return true}
function b(){console.log('b');return false}
function c(){console.log('c');return undefined}
function d(){console.log('d');return null}

result = a() && b()      // a b false
result = a() & b()       // a b 0

result = a() ||  b()     // a true
result = a() |  b()      // a b 1

result = a() || b() && c() || d()       // 等价于 a() || ''  ,所以 a true
result = a() &&  b() || c()  && d()     // a b c undefined
result = (a() ||  b()) &&  c() || d()   // a c d null
result = (a() ||  b()) && (c() || d())  // a c d null
result = a() ||  b()  && (c() || d())   // a true
result = a() ||  (b() && c()) || d()    // a true

条件运算符

JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符。

语法:variablename = (condition)?value1:value2;

//如果变量 age 中的值小于 18,则向变量 voteable 赋值 "年龄太小",否则赋值 "年龄已达到"。
voteable = (age < 18) ? "年龄太小" : "年龄已达到";

JavaScript 语句

if else 条件语句

条件语句用于基于不同的条件来执行不同的动作。

在写代码时,需要为不同的决定来执行不同的动作。可以在代码中使用条件语句来完成该任务。

在 JavaScript 中,我们可使用以下条件语句:

  • if 语句                              ----- 只有当指定条件为 true 时,使用该语句来执行代码
  • if...else 语句                    ----- 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • if...else if....else 语句     ----- 使用该语句来选择多个代码块之一来执行
  • switch 语句                     ----- 使用该语句来选择多个代码块之一来执行
// if 语句
if (condition)
{
    // 当条件为 true 时执行的代码
}

// if else 语句
if (condition)
{
    // 当条件为 true 时执行的代码
}
else
{
    // 当条件不为 true 时执行的代码
}

// if else if else 语句
if (condition1)
{
    // 当条件 1 为 true 时执行的代码
}
else if (condition2)
{
    // 当条件 2 为 true 时执行的代码
}
else
{
    // 当条件 1 和 条件 2 都不为 true 时执行的代码
}

switch 语句

switch 语句用于基于不同的条件来执行不同的动作。

switch(n)
{
    case 1:
        // 执行代码块 1
        break;
    case 2:
        // 执行代码块 2
        break;
    default:
        // 与 case 1 和 case 2 不同时执行的代码
}

工作原理:首先设置表达式 n(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 break 来阻止代码自动地向下一个 case 运行。

实例:显示今天的星期名称。请注意 Sunday=0, Monday=1, Tuesday=2, 等等:

var d = new Date().getDay();
switch (d) {
    case 0:
        x = "今天是星期日";
        break;
    case 1:
        x = "今天是星期一";
        break;
    case 2:
        x = "今天是星期二";
        break;
    case 3:
        x = "今天是星期三";
        break;
    case 4:
        x = "今天是星期四";
        break;
    case 5:
        x = "今天是星期五";
        break;
    case 6:
        x = "今天是星期六";
        break;
}
console.log(x)

default 关键词来规定匹配不存在时做的事情:

var d = new Date().getDay();
switch (d) {
    case 6:
        x = "今天是星期六";
        break;
    case 0:
        x = "今天是星期日";
        break;
    default:
        x = "期待周末";
}
console.log(x)

for 循环

JavaScript 支持不同类型的循环:

  • for - 循环代码块一定的次数
  • for/in - 循环遍历对象的属性
  • while - 当指定的条件为 true 时循环指定的代码块
  • do/while - 同样当指定的条件为 true 时循环指定的代码块

for 循环是您在希望创建循环时常会用到的工具。

for 循环的语法:
        for (语句1; 语句2; 语句3)
        {
            被执行的代码块
        }
说明:
        语句1 (代码块)开始前执行
        语句2 定义运行循环(代码块)的条件
        语句3 在循环(代码块)已被执行之后执行

使用 for 循环

for (var i = 0; i < cars.length; i++) {
    document.write(cars[i] + "<br>");
}
for (var i = 0; i < 5; i++) {
    x = x + "该数字为 " + i + "<br>";
}

语句1
        通常使用 语句1 初始化循环中所用的变量 (var i=0)。
        语句1 是可选的,也就是说不使用 语句1 也可以。
        可以在 语句1 中初始化任意(或者多个)值
语句2
        通常 语句2 用于评估初始变量的条件。
        如果 语句2 返回 true,则循环再次开始,如果返回 false,则循环将结束。
        语句2 同样是可选的。        
        如果省略了 语句2,那么必须在循环内提供 break。否则循环就无法停下来。这样有可能令浏览器崩溃。
语句3
        通常 语句3 会增加初始变量的值。
        语句3 也是可选的。
        语句3 有多种用法。增量可以是负数 (i--),或者更大 (i=i+15)。
        语句3 也可以省略(比如当循环内部有相应的代码时)

for (var i = 0, len = cars.length; i < len; i++) {
    document.write(cars[i] + "<br>");
}

// 省略 语句1
var i = 2, len = cars.length;
for (; i < len; i++) {
    document.write(cars[i] + "<br>");
}

// 省略 语句3
var i = 0, len = cars.length;
for (; i < len;) {
    document.write(cars[i] + "<br>");
    i++;
}

for / In 循环

JavaScript 的 for / in 语句循环遍历对象的属性。( 在有关 JavaScript 对象的章节学到更多有关 for / in 循环的知识。 )

var person = {fname: "Bill", lname: "Gates", age: 56};

var txt = '';
for (x in person)  // x 为属性名
{
    txt = txt + person[x];
}
console.log(txt)

while 循环

while 循环会在指定条件为真时循环执行代码块。

语法:
        while (条件)
        {
            // 需要执行的代码
        }

实例:本例中的循环将继续运行,只要变量 i 小于 5:

var i = 1, x = '';
while (i < 5) {
    x = x + "The number is " + i + "<br>" + '\n';
    i++;
}
console.log(x)

do/while 循环

do/while 循环是 while 循环的变体。该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。

语法:
        do
        {
            // 需要执行的代码
        }
        while (条件);

实例:下面的例子使用 do/while 循环。该循环至少会执行一次,即使条件为 false 它也会执行一次,因为代码块会在条件被测试前执行:

var x = '';
do {
    x = x + "The number is " + i + "<br>";
    i++;
}
while (i < 5);

比较 for 和 while

while 循环与 for 循环很像。

使用 for 循环来显示 cars 数组中的所有值:

cars = ["BMW", "Volvo", "Saab", "Ford"];
var i = 0;
for (; cars[i];) {
    document.write(cars[i] + "<br>");
    i++;
}

使用 while 循环来显示 cars 数组中的所有值:

cars = ["BMW", "Volvo", "Saab", "Ford"];
var i = 0;
while (cars[i]) {
    document.write(cars[i] + "<br>");
    i++;
}

break 和 continue 语句

  • break 语句用于跳出循环。
  • continue 用于跳过循环中的一个迭代。

typeof, null, 和 undefined

typeof

typeof 操作符:你可以使用 typeof 操作符来检测变量的数据类型。

在JavaScript中,数组是一种特殊的对象类型。 因此 typeof [1,2,3,4] 返回 object。 

console.log(typeof "John")                // 返回 string
console.log(typeof 3.14)                  // 返回 number
console.log(typeof false)                 // 返回 boolean
console.log(typeof [1,2,3,4])             // 返回 object
console.log(typeof {name:'John', age:34}) // 返回 object

null

在 JavaScript 中 null 表示 "什么都没有"。

null 是一个只有一个值的特殊类型。表示一个空对象引用。用 typeof 检测 null 返回是object

可以设置为 null 来清空对象:

var person = null;           // 值为 null(空), 但类型为对象

undefined

在 JavaScript 中, undefined 是一个没有设置值的变量。typeof 一个没有值的变量会返回 undefined

var person;    // 值为 undefined(空), 类型是undefined

任何变量都可以通过设置值为 undefined 来清空。 类型为 undefined.

person = undefined;    // 值为 undefined, 类型是undefined

undefined 和 null 的区别

null 和 undefined 的值相等,但类型不等

typeof undefined             // undefined
typeof null                  // object
null === undefined           // false  (=== 表示绝对相等:"值"和"类型"都相等)
null == undefined            // true   (== 表示值相等)

1、定义

  •  (1)undefined:是所有没有赋值变量的默认值,自动赋值。
  •  (2)null:主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址。

2、何时使用null?

当使用完一个比较大的对象时,需要对其进行释放内存时,设置为 null。

3、null 与 undefined 的异同点是什么呢?

共同点:都是原始类型,保存在栈中变量本地。

不同点:

  • (1)undefined : 表示变量声明过但并未赋过值。它是所有未赋值变量默认值,例如:var a; // a 自动被赋值为 undefined
  • (2)null :表示一个变量将来可能指向一个对象。一般用于主动释放指向对象的引用,例如:var emps = ['ss','nn']; emps = null; // 释放指向数组的引用

4、延伸 ----- 垃圾回收站

它是专门释放对象内存的一个程序。

  •  (1)在底层,后台伴随当前程序同时运行;引擎会定时自动调用垃圾回收期;
  •  (2)总有一个对象不再被任何变量引用时,才释放。

JavaScript 正则表达式

JS正则表达式语法大全(非常详细)JS正则表达式语法大全(非常详细)

JS正则表达式完整版:JS正则表达式完整版_Chafferer,迷心的博客-CSDN博客

JavaScript 正则表达式:JavaScript 正则表达式 | 菜鸟教程

语法(其中修饰符是可选的):/正则表达式主体/修饰符(可选)

实例:var patt = /runoob/i

实例解析:

  • /runoob/i  是一个正则表达式。
  • runoob  是一个正则表达式主体 (用于检索)。
  • i  是一个修饰符 (搜索不区分大小写)。

字符串方法 search 和 replace

在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()。

  • search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
  • replace() 方法 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

search() 方法replace() 方法 使用正则表达式:

// 使用正则表达式搜索 "Runoob" 字符串,且不区分大小写:
var str = "Visit Runoob!";
var str_index = str.search(/Runoob/i);
console.log(str_index)

// 使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :
// var str = document.getElementById("demo").innerHTML;
var str = 'this is a [microsoft] test'
var txt = str.replace(/microsoft/i, "Runoob");
console.log(txt)

使用字符串作为参数时,字符串参数会转换为正则表达式:

// 检索字符串中 "Runoob" 的子串:
var str = "Visit Runoob!";
var n = str.search("Runoob");

var str = document.getElementById("demo").innerHTML;
var txt = str.replace("Microsoft","Runoob");

正则表达式修饰符

修饰符 可以在全局搜索中不区分大小写:

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。

正则表达式模式

方括号用于查找某个范围内的字符:

表达式 描述
[abc] 查找方括号之间的任何字符。
[0-9] 查找任何从 0 至 9 的数字。
(x|y) 查找任何以 | 分隔的选项。

元字符是拥有特殊含义的字符:

元字符 描述
\d 查找数字。
\s 查找空白字符。
\b 匹配单词边界。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

量词:

量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。

使用 RegExp 对象

在 JavaScript 中,RegExp 对象是一个预定义了属性和方法的正则表达式对象。

使用 test():

test() 方法是一个正则表达式方法。

test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

以下实例用于搜索字符串中的字符 "e"。实例:

var patt = /e/;
patt.test("The best things in life are free!"); // 返回 true

也可以不用设置正则表达式的变量,以上两行代码可以合并为一行:/e/.test("The best things in life are free!")

使用 exec()

exec() 方法是一个正则表达式方法。

exec() 方法用于检索字符串中的正则表达式的匹配。

该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

以下实例用于搜索字符串中的字母 "e":/e/.exec("The best things in life are free!");

示例:



var testStr = "now test001 test002";
var re = /test(\d+)/ig;
var r = "";
while(r = re.exec(testStr)) {
    console.log(r[0] + "  " + r[1]);
}

var tempArray = /<([^>]*?)>/.exec("<div>>测试div<span>>");
console.log("长度 : " + tempArray.length);
for (var index = 0; index < tempArray.length; index++) {
    console.log(index + " = " + tempArray[index]);
}

re = "<div>>测试div<span>>".match(/<([^>]*?)>/g)
console.log("长度 : " + tempArray.length);
for (var index = 0; index < tempArray.length; index++) {
    console.log(re[index])
}

更多实例

完整的 RegExp 参考手册

完整的 RegExp 对象参考手册,请参考我们的 JavaScript RegExp 参考手册

该参考手册包含了所有 RegExp 对象的方法和属性。

JavaScript 的错误捕获 --- throw、try 和 catch

  • try 语句测试代码块的错误。
  • catch 语句处理错误。
  • throw 语句创建自定义错误。
  • finally 语句在 try 和 catch 语句之后,无论是否有触发异常,该语句都会执行。

try 和 catch

try 语句允许我们定义在执行时进行错误测试的代码块。

catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。

JavaScript 语句 try 和 catch 是成对出现的。

语法:
        try {
            //异常的抛出
        } catch(e) {
            //异常的捕获与处理
        } finally {
            //结束处理
        }

实例:在下面的例子中,我们故意在 try 块的代码中写了一个错字。catch 块会捕捉到 try 块中的错误,并执行代码来处理它。

var txt = "";

function message() {
    try {
        adddlert("Welcome guest!");
    } catch (err) {
        txt = "本页有一个错误。\n\n";
        txt += "错误描述:" + err.message + "\n\n";
        txt += "点击确定继续。\n\n";
        console.log(txt);
    }
}
message()

finally 语句

finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。

function myFunction() {
    var message, x;
    message = document.getElementById("p01");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try {
        if(x == "") throw "值是空的";
        if(isNaN(x)) throw "值不是一个数字";
        x = Number(x);
        if(x > 10) throw "太大";
        if(x < 5) throw "太小";
    }
    catch(err) {
        message.innerHTML = "错误: " + err + ".";
    }
    finally {
        document.getElementById("demo").value = "";
    }
}

Throw 语句

throw 语句允许我们创建自定义错误。

正确的技术术语是:创建或抛出异常(exception)。

如果把 throw 与 try 和 catch 一起使用,那么您能够控制程序流,并生成自定义的错误消息。

语法:throw exception

异常可以是 JavaScript 字符串、数字、逻辑值或对象。

实例:本例检测输入变量的值。如果值是错误的,会抛出一个异常(错误)。catch 会捕捉到这个错误,并显示一段自定义的错误消息:

function myFunction() {
    var message, x;
    message = document.getElementById("message");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try {
        if(x == "")  throw "值为空";
        if(isNaN(x)) throw "不是数字";
        x = Number(x);
        if(x < 5)    throw "太小";
        if(x > 10)   throw "太大";
    }
    catch(err) {
        message.innerHTML = "错误: " + err;
    }
}

JavaScript 使用误区

赋值运算符应用错误

在 JavaScript 程序中如果你在 if 条件语句中使用赋值运算符的等号 (=) 将会产生一个错误结果, 正确的方法是使用比较运算符的两个等号 (==)。

if 条件语句返回 false (是我们预期的) 因为 x 不等于 10:

var x = 0;
if (x == 10){
    console.log("相等");
}else {
    console.log("不相等");
}

比较运算符常见错误

在常规的比较中,数据类型被忽略的,以下 if 条件语句返回 true:

var x = 10;
var y = "10";
if (x == y){
    console.log("相等");
}else {
    console.log("不相等");
}

在严格的比较运算中,=== 为恒等计算符,同时检查表达式的 类型,以下 if 条件语句返回 false:

var x = 10;
var y = "10";
if (y === x){
    console.log("相等");
}else {
    console.log("不相等");
}

这种错误经常会在 switch 语句中出现,switch 语句会使用恒等计算符(===)进行比较:

加法与连接注意事项

加法 是两个数字相加。

连接 是两个字符串 连接。

JavaScript 的加法和连接都使用 + 运算符。

接下来我们可以通过实例查看 两个数字相加 及 数字与字符串连接 的区别:

var x = 10 + 5;          // x 的结果为 15
var x = 10 + "5";        // x 的结果为 "105"

使用变量相加结果也不一致:

var x = 10;
var y = 5;
var z = x + y;           // z 的结果为 15

var x = 10;
var y = "5";
var z = x + y;           // z 的结果为 "105"

浮点型数据使用注意事项

JavaScript 中的所有数据都是以 64 位浮点型数据(float) 来存储。

所有的编程语言,包括 JavaScript,对浮点型数据的精确度都很难确定:

var x = 0.1;
var y = 0.2;
var z = x + y     // z 的结果为 0.30000000000000004
if (z == 0.3)     // 返回 false

为解决以上问题,可以用整数的乘除法来解决:

var z = (x * 10 + y * 10) / 10;     // z 的结果为 0.3

更多内容可以参考:JavaScript 中精度问题以及解决方案

JavaScript 字符串分行

JavaScript 允许我们在字符串中使用断行语句:

正确示例:

var x =
"Hello World!";

错误示例:在字符串中直接使用回车换行是会报错的:

var x = "Hello
World!";

正确示例:字符串断行需要使用反斜杠(\),如下所示:

var x = "Hello \
World!";

错误的使用分号

在 JavaScript 中,分号是可选的

由于 return 是一个完整的语句,所以 JavaScript 将关闭 return 语句。

以下实例中,if 语句失去方法体,原 if 语句的方法体作为独立的代码块被执行,导致错误的输出结果。

由于分号使用错误,if 语句中的代码块就一定会执行:

if (x == 19);
{
    // code block 
}

return 语句使用注意事项

JavaScript 默认是在代码的最后一行自动结束。

以下两个实例返回结果是一样的 ( 一个有分号,一个没有 ):

实例 1 :( 没有分号 )

function myFunction(a) {
    var power = 10 
    return a * power
}

实例 2 ( 有分号 )

function myFunction(a) {
    var power = 10;
    return a * power;
}

JavaScript 也可以使用多行来结束一个语句。以下实例返回相同的结果。示例 3

function myFunction(a) {
    var
    power = 10; 
    return a * power;
}

数组中使用名字来索引 ( 错误用法 )

  • 许多程序语言都允许使用名字来作为数组的索引。使用名字来作为索引的数组称为 关联数组 (或 哈希 )。
  • JavaScript 不支持使用名字来索引数组,只允许使用数字索引。

实例

var person = [];
person[0] = "John";
person[1] = "Doe";
person[2] = 46;
var x = person.length;         // person.length 返回 3
var y = person[0];             // person[0] 返回 "John"

在 JavaScript 中, 对象 使用 名字作为索引

当访问数组时,如果使用名字作为索引,JavaScript 会把数组重新定义为标准对象。执行这样操作后,数组的方法及属性将不能再使用,否则会产生错误:

var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
var x = person.length;         // person.length 返回 0
var y = person[0];             // person[0] 返回 undefined

定义数组元素,最后不能添加逗号

数组最后一个值的后面添加逗号虽然语法没有问题,但是在不同的浏览器可能得到不同的结果。

  • 错误的方式:var colors = [5, 6, 7,];    // 这样数组的长度可能为3 也可能为4。
  • 正确的定义方式:points = [40, 100, 1, 5, 25, 10];

定义对象,最后不能添加逗号

  • 错误的定义方式:websites = {site:"菜鸟教程", url:"www.runoob.com", like:460,}
  • 正确的定义方式:websites = {site:"菜鸟教程", url:"www.runoob.com", like:460}

Undefined 不是 Null

在 JavaScript 中, 

  • null 用于 对象
  • undefined 用于变量,属性和方法。

对象只有被定义才有可能为 null,否则为 undefined。

如果我们想测试对象是否存在,在对象还没定义时将会抛出一个错误。

  • 错误的使用方式:if (myObj !== null && typeof myObj !== "undefined")
  • 正确的方式是先使用 typeof 来检测对象是否已定义:if (typeof myObj !== "undefined" && myObj !== null)

程序块作用域

在每个代码块中 JavaScript 不会创建一个新的作用域,一般各个代码块的作用域都是全局的。

以下代码的的变量 i 返回 10,而不是 undefined:

for (var i = 0; i < 10; i++) {
    // some code
}
return i;

JavaScript 表单

JavaScript 表单验证

HTML 表单验证可以通过 JavaScript 来完成。

以下实例代码用于判断表单字段(fname)值是否存在, 如果不存在,就弹出信息,阻止表单提交:

JavaScript 实例

function validateForm() {
    var x = document.forms["myForm"]["fname"].value;
    if (x == null || x == "") {
        alert("需要输入名字。");
        return false;
    }
}

以上 JavaScript 代码可以通过 HTML 代码来调用。

HTML 表单实例。示例:尝试一下 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function validateForm() {
    var x = document.forms["myForm"]["fname"].value;
    if (x == null || x == "") {
        alert("需要输入名字。");
        return false;
    }
}
</script>
</head>
<body>

<form name="myForm" action="demo_form.php"
onsubmit="return validateForm()" method="post">
名字: <input type="text" name="fname">
<input type="submit" value="提交">
</form>

</body>
</html>

HTML 表单自动验证

HTML 表单验证也可以通过浏览器来自动完成。

如果表单字段 (fname) 的值为空, required 属性会阻止表单提交:

实例

<form action="demo_form.php" method="post">
  <input type="text" name="fname" required="required">
  <input type="submit" value="提交">
</form>

示例:尝试一下

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>

<form action="demo_form.php" method="post">
  <input type="text" name="fname" required="required">
  <input type="submit" value="提交">
</form>

<p>点击提交按钮,如果输入框是空的,浏览器会提示错误信息。</p>

</body>
</html>

数据验证

数据验证用于确保用户输入的数据是有效的。

典型的数据验证有:

  • 必需字段是否有输入?
  • 用户是否输入了合法的数据?
  • 在数字字段是否输入了文本?

大多数情况下,数据验证用于确保用户正确输入数据。

数据验证可以使用不同方法来定义,并通过多种方式来调用。

服务端数据验证是在数据提交到服务器上后再验证。

客户端数据验证是在数据发送到服务器前,在浏览器上完成验证。

HTML 约束验证

HTML5 新增了 HTML 表单的验证方式:约束验证(constraint validation)。

约束验证是表单被提交时浏览器用来实现验证的一种算法。

HTML 约束验证基于:

  • HTML 输入属性
  • CSS 伪类选择器
  • DOM 属性和方法

约束验证 HTML 输入属性

属性 描述
disabled 规定输入的元素不可用
max 规定输入元素的最大值
min 规定输入元素的最小值
pattern 规定输入元素值的模式
required 规定输入元素字段是必需的
type  规定输入元素的类型

完整列表,请查看 HTML 输入属性

约束验证 CSS 伪类选择器

选择器 描述
:disabled 选取属性为 "disabled" 属性的 input 元素
:invalid 选取无效的 input 元素
:optional 选择没有"required"属性的 input 元素
:required 选择有"required"属性的 input 元素
:valid 选取有效值的 input 元素

完整列表,请查看 CSS 伪类

JavaScript 表单验证

JavaScript 表单验证

JavaScript 可用来在数据被送往服务器前对 HTML 表单中的这些输入数据进行验证。

表单数据经常需要使用 JavaScript 来验证其正确性:

  • 验证表单数据是否为空?
  • 验证输入是否是一个正确的email地址?
  • 验证日期是否输入正确?
  • 验证表单输入内容是否为数字型?

必填(或必选)项目

下面的函数用来检查用户是否已填写表单中的必填(或必选)项目。假如必填或必选项为空,那么警告框会弹出,并且函数的返回值为 false,否则函数的返回值则为 true(意味着数据没有问题):

function validateForm()
{
    var x=document.forms["myForm"]["fname"].value;
    if (x==null || x=="")
    {
        alert("姓必须填写");
        return false;
    }
}

以上函数在 form 表单提交时被调用:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<head>
<script>
function validateForm(){
var x=document.forms["myForm"]["fname"].value;
if (x==null || x==""){
  alert("姓必须填写");
  return false;
  }
}
</script>
</head>
<body>
	
<form name="myForm" action="demo-form.php" onsubmit="return validateForm()" method="post">
姓: <input type="text" name="fname">
<input type="submit" value="提交">
</form>
	
</body>
</html>

实例:尝试一下 »

E-mail 验证

下面的函数检查输入的数据是否符合电子邮件地址的基本语法。

意思就是说,输入的数据必须包含 @ 符号和点号(.)。同时,@ 不可以是邮件地址的首字符,并且 @ 之后需有至少一个点号:

function validateForm(){
    var x=document.forms["myForm"]["email"].value;
    var atpos=x.indexOf("@");
    var dotpos=x.lastIndexOf(".");
    if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){
        alert("不是一个有效的 e-mail 地址");
        return false;
    }
}

下面是连同 HTML 表单的完整代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<head>
<script>
function validateForm(){
	var x=document.forms["myForm"]["email"].value;
	var atpos=x.indexOf("@");
	var dotpos=x.lastIndexOf(".");
	if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){
		alert("不是一个有效的 e-mail 地址");
  		return false;
	}
}
</script>
</head>
<body>
	
<form name="myForm" action="demo-form.php" onsubmit="return validateForm();" method="post">
Email: <input type="text" name="email">
<input type="submit" value="提交">
</form>
	
</body>
</html>

示例::尝试一下

JavaScript 验证 API

  • 约束验证 DOM 方法
  • 约束验证 DOM 属性
  • Validity 属性

JavaScript 验证 API | 菜鸟教程

JavaScript 保留关键字

在 JavaScript 中,一些标识符是保留关键字,不能用作变量名或函数名。

JavaScript 保留关键字 | 菜鸟教程

JSON

JSON 是用于存储和传输数据的格式。JSON 通常用于服务端向网页传递数据 。

什么是 JSON?

  • JSON 英文全称 JavaScript Object Notation
  • JSON 是一种轻量级的数据交换格式。
  • JSON是独立的语言 *
  • JSON 易于理解。

JSON 使用 JavaScript 语法,但是 JSON 格式仅仅是一个文本。文本可以被任何编程语言读取及作为数据格式传递。

JSON 实例

以下 JSON 语法定义了 sites 对象: 3 条网站信息(对象)的数组:

{"sites":[
    {"name":"Runoob", "url":"www.runoob.com"}, 
    {"name":"Google", "url":"www.google.com"},
    {"name":"Taobao", "url":"www.taobao.com"}
]}

JSON 格式化后为 JavaScript 对象

JSON 格式在语法上与创建 JavaScript 对象代码是相同的。

由于它们很相似,所以 JavaScript 程序可以很容易的将 JSON 数据转换为 JavaScript 对象。

JSON 语法规则

  • 数据为 键/值 对。
  • 数据由逗号分隔。
  • 大括号保存对象
  • 方括号保存数组

JSON 数据 - 一个名称对应一个值

JSON 数据格式为 键/值 对,就像 JavaScript 对象属性。

键/值对包括字段名称(在双引号中),后面一个冒号,然后是值:"name":"Runoob"

JSON 对象

JSON 对象保存在大括号内。

就像在 JavaScript 中, 对象可以保存多个 键/值 对:{"name":"Runoob", "url":"www.runoob.com"}

JSON 数组

JSON 数组保存在中括号内。

就像在 JavaScript 中, 数组可以包含对象:

"sites":[
    {"name":"Runoob", "url":"www.runoob.com"}, 
    {"name":"Google", "url":"www.google.com"},
    {"name":"Taobao", "url":"www.taobao.com"}
]

在以上实例中,对象 "sites" 是一个数组,包含了三个对象。每个对象为站点的信息(网站名和网站地址)。

JSON 字符串转换为 JavaScript 对象

通常我们从服务器中读取 JSON 数据,并在网页中显示数据。

简单起见,我们网页中直接设置 JSON 字符串 (你还可以阅读我们的 JSON 教程):

首先,创建 JavaScript 字符串,字符串为 JSON 格式的数据:

var text = '{ "sites" : [' +
'{ "name":"Runoob" , "url":"www.runoob.com" },' +
'{ "name":"Google" , "url":"www.google.com" },' +
'{ "name":"Taobao" , "url":"www.taobao.com" } ]}';

然后,使用 JavaScript 内置函数 JSON.parse() 将字符串转换为 JavaScript 对象:var obj = JSON.parse(text);

最后,在你的页面中使用新的 JavaScript 对象:

var text = '{ "sites" : [' +
    '{ "name":"Runoob" , "url":"www.runoob.com" },' +
    '{ "name":"Google" , "url":"www.google.com" },' +
    '{ "name":"Taobao" , "url":"www.taobao.com" } ]}';
    
obj = JSON.parse(text);
document.getElementById("demo").innerHTML = obj.sites[1].name + " " + obj.sites[1].url;

相关函数

函数 描述
JSON.parse() 用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify() 用于将 JavaScript 值转换为 JSON 字符串。

更多 JSON 信息,你可以阅读我们的 JSON 教程

JSONP 跨域数据交互

什么是同源

如果两个页面的协议域名端口都相同,则两个页面具有相同的源
例如,下表给出了相对于 http://www.test.com/index.html 页面的同源检测:

什么是同源策略

同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。

MDN 官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

通俗的理解:浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互,例如:

  • 无法读取非同源网页的 CookieLocalStorage 和 IndexedDB
  • 无法接触非同源网页的 DOM
  • 无法向非同源地址发送 AJAX 请求

什么是跨域

同源指的是两个 URL 的协议域名端口一致,反之,则是跨域

出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。

网页:http://www.test.com/index.html

接口:http://www.api.com/userlist

浏览器对跨域请求的拦截

浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!

如何实现跨域数据请求

实现跨域数据请求方法有很多,比如JSONPCORSpostMessageWebsocketNginx反向代理window.name + iframe 、document.domain + iframelocation.hash + iframe等。其中最主要的三种解决方案,分别是 JSONP 和 CORS 和 Nginx 反向代理。

  • JSONP:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持 GET 请求,不支持 POST 请求。
  • CORS:出现的较晚,它是 W3C 标准,属于跨域 AJAX 请求的根本解决方案。支持 GET 和 POST 请求。缺点是不兼容某些低版本的浏览器。
  • Nginx反向代理:同源策略对服务器不加限制,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

JSONP 原理

JSONP(JSON with Padding)是一种跨域数据交互的技术,主要用于解决浏览器的同源策略限制问题。它通过动态创建 <script> 标签来实现跨域请求,服务器会返回一段 JavaScript 代码,其中包含客户端指定的回调函数和数据。

JSONP 原理:事先定义一个用于获取跨域响应数据的回调函数,并通过没有同源策略限制的script标签发起一个请求(将回调函数的名称放到这个请求的query参数里),然后服务端返回这个回调函数的执行,并将需要响应的数据放到回调函数的参数里,前端的script标签请求到这个执行的回调函数后会立马执行,于是就拿到了执行的响应数据。

以下是 JSONP 的关键点:

工作原理:

  • 客户端定义一个回调函数,比如 handleResponse(data)。
  • 动态创建 <script> 标签,并将回调函数名作为参数附加到请求 URL 上,例如:https://example.com/data?callback=handleResponse。
  • 服务器接收到请求后,返回一段 JavaScript 代码,内容为 handleResponse({...}),其中 {...} 是实际的数据。
  • 浏览器执行这段脚本,调用回调函数并传入数据。

优点:

  • 兼容性好,支持所有现代浏览器及旧版浏览器。它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制。
  • 在请求完毕后可以通过调用callback的方式回传结果
  • 简单易用,无需复杂的配置或 CORS 支持。

缺点:

  • 只能发送 GET 请求,不能发送 POST 或其他类型的 HTTP 请求。
  • 只能跨域 HTTP 请求,不能解决不同域的两个页面之间如何进行JavaScript 调用的问题
  • 存在安全风险,因为 JSONP 本质上是执行任意的 JavaScript 代码,可能会导致 XSS 攻击。
  • 无法检测请求失败或超时(除非手动设置超时机制)。

使用场景:

  • 当需要从不支持 CORS 的旧服务获取数据时。
  • 在需要兼容老旧浏览器的情况下。

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSONP Example</title>
    <script>
        function handleResponse(data) {
            console.log('Received data:', data);
            document.getElementById('output').innerText = JSON.stringify(data, null, 2);
        }

        window.onload = function () {
            var script = document.createElement('script');
            script.src = 'https://example.com/data?callback=handleResponse';
            document.body.appendChild(script);
        };
    </script>
</head>
<body>
    <h1>JSONP Data Output:</h1>
    <pre id="output">Loading...</pre>
</body>
</html>

安全性建议:

  • 尽量使用 CORS 替代 JSONP,CORS 更加安全且功能更强大。
  • 如果必须使用 JSONP,请确保只信任来源可靠的服务提供方。
  • 避免将敏感信息暴露给不可信的第三方服务。

替代方案:

  • CORS (Cross-Origin Resource Sharing):现代浏览器广泛支持的跨域资源共享标准,比 JSONP 更安全、更灵活。
  • 代理服务器:通过自己的服务器作为中间人,向目标服务器发起请求,然后将结果返回给前端,绕过浏览器的同源策略限制。

JSONP 应用流程

设定一个script标签

<script src="http://jsonp.js?callback=cb"></script>

// 或

let script = document.createElement('script');
script.src = "http://jsonp.js?callback=cb";
body.append(script)

callback定义了一个函数名,而远程服务端通过调用指定的函数并传入参数来实现传递参数,将function(response)传递回客户端

router.get('/', function (req, res, next) {
    (() => {
        const data = {
            x: 10
        };
        let params = req.query;
        if (params.callback) {
            let callback = params.callback;
            console.log(params.callback);
            res.send(`${callback}(${JSON.stringify(data.x)})`);
        } else {
            res.send('err');
        }
    })();
});

客户端接收到返回的 JS 脚本,开始解析和执行function(response)

JSONP 实现

一个简单的 JSONP 实现,其实就是拼接URL,然后将动态添加一个script元素到头部。

前端 JSONP 方法示例:

function jsonp(req) {
    var script = document.createElement('script');
    var url = req.url + '?callback=' + req.callback.name;
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}

前端 JS 示例:

function hello(res){
    alert('hello ' + res.data);
}
jsonp({
    url : '',
    callback : hello 
});

服务器端代码:

var http = require('http');
var urllib = require('url');

var port = 8080;
var data = {'data':'world'};

http.createServer(function(req,res){
    var params = urllib.parse(req.url,true);
    if(params.query.callback){
        console.log(params.query.callback);
        // jsonp
        var str = params.query.callback + '(' + JSON.stringify(data) + ')';
        res.end(str);
    } else {
        res.end();
    }
    
}).listen(port,function(){
    console.log('jsonp server is on');
});

可靠的 JSONP 实例

(function (global) {
    var id = 0,
        container = document.getElementsByTagName("head")[0];

    function jsonp(options) {
        if(!options || !options.url) return;

        var scriptNode = document.createElement("script"),
            data = options.data || {},
            url = options.url,
            callback = options.callback,
            fnName = "jsonp" + id++;

        // 添加回调函数
        data["callback"] = fnName;

        // 拼接url
        var params = [];
        for (var key in data) {
            params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
        }
        url = url.indexOf("?") > 0 ? (url + "&") : (url + "?");
        url += params.join("&");
        scriptNode.src = url;

        // 传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法
        global[fnName] = function (ret) {
            callback && callback(ret);
            container.removeChild(scriptNode);
            delete global[fnName];
        }

        // 出错处理
        scriptNode.onerror = function () {
            callback && callback({error:"error"});
            container.removeChild(scriptNode);
            global[fnName] && delete global[fnName];
        }

        scriptNode.type = "text/javascript";
        container.appendChild(scriptNode)
    }

    global.jsonp = jsonp;

})(this);

使用示例:

jsonp({
    url : "www.example.com",
    data : {id : 1},
    callback : function (ret) {
        console.log(ret);
    }
});

JSONP安全性问题

CSRF攻击

前端构造一个恶意页面,请求JSONP接口,收集服务端的敏感信息。如果JSONP接口还涉及一些敏感操作或信息(比如登录、删除等操作),那就更不安全了。

解决方法:验证JSONP的调用来源(Referer),服务端判断 Referer 是否是白名单,或者部署随机 Token 来防御。

XSS漏洞

不严谨的 content-type 导致的 XSS 漏洞,想象一下 JSONP 就是你请求 http://abc.com?callback=douniwan, 然后返回 douniwan({ data }),那假如请求 http://abc.com?callback=<script>alert(1)</script> 不就返回 <script>alert(1)</script>({ data })了吗,如果没有严格定义好 Content-Type( Content-Type: application/json ),再加上没有过滤 callback 参数,直接当 HTML 解析了,就是一个赤裸裸的 XSS 了。

解决方法:严格定义 Content-Type: application/json,然后严格过滤 callback 后的参数并且限制长度(进行字符转义,例如<换成&lt>换成&gt)等,这样返回的脚本内容会变成文本格式,脚本将不会执行。

服务器被黑,返回一串恶意执行的代码

可以将执行的代码转发到服务端进行校验 JSONP 内容校验,再返回校验结果。

javascript:void(0) 含义

void() 仅仅是代表不返回任何值但是括号内的表达式还是要运行,如:void(alert("Warnning!"))

javascript:void(0) 中最关键的是 void 关键字, void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。

语法格式如下:

void func()
javascript:void func()

或者

void(func())
javascript:void(func())

示例:(  尝试一下 )下面的代码创建了一个超级链接,当用户点击以后不会发生任何事。

<!DOCTYPE html> 
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob.com)</title> 
</head> 
<body>
	
    <a href="javascript:void(0)">单此处什么也不会发生</a>
	
</body>
</html>

当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。以下实例中,在用户点击链接后显示警告信息:( 尝试一下 )

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
	
<p>点击以下链接查看结果:</p>
<a href="javascript:void(alert('Warning!!!'))">点我!</a>
	
</body>
</html>

以下实例中参数 a 将返回 undefined :

function getValue(){
   var a,b,c;
   a = void ( b = 5, c = 7 );
   document.write('a = ' + a + ' b = ' + b +' c = ' + c );
}

href="#"与href="javascript:void(0)"的区别

  • # 包含了一个位置信息,默认的锚是#top 也就是网页的上端。在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id
  • 而 javascript:void(0), 仅仅表示一个死链接。

示例:( 尝试一下 )

<a href="javascript:void(0);">点我没有反应的!</a>
<a href="#pos">点我定位到指定位置!</a>
<br>
...
<br>
<p id="pos">尾部定位点</p>

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐