语言基础(变量、数据类型) -----JavaScript整理、复习、梳理

JavaScript语法

区分大小写

在JavaScript中一切都区分大小写。无论是变量、函数、操作符都区分大小写。

例如:test和Test是两个不同的变量

标识符

所谓的标识符就指变量、函数、属性、函数参数的名称。

标识符可以由一或多个字符组成
第一个字符必须是一个字母、下划线、美元符号
剩下的其他字符可以说字母、下划线、美元符号、数字
按照惯例标识符一般使用驼峰大小写形式,即第一个单词首字母小写,后面的每个单词首字母大写
虽然这种写法不是强制的,因为这种写法跟JavaScript内置函数和对象命名方式是一致的,所以推荐
例子:firstSecond、myCar、

注释

JavaScript在注释代码采用C语言的风格包括单行和多行注释

// 单行注释

/*
多行注释
*/

注释快捷键:/+Ctrl

严格模式

严格模式是一种不同的JavaScript解析和执行模型,对于不规范的写法在这种模式下会被处理,对于不安全的活动将抛出错误。若对整个脚本启用严格模式,需要在脚本开头加上

"use strict";

同时严格模式也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头就可以:

function doSomething() {
    
    
	"use strict";
	//函数体
}

语句

JavaScript中语句以分号结尾,省略号意味着由解析器确定语句在哪里结尾。

let sum = a + b    //不推荐
let diff = a + b;    //推荐
  1. 添加分号有助于防止省略造成的问题;
  2. 可以避免因为输入内容不完整造成的问题;
  3. 同时添加分号也有利于删除空行压缩代码;
  4. 在某些情况下也有助于提升性能(解析器会在何时的位置补上分号)
    多个语句可以合并到一个C语言风格的代码块中。代码块由左花括号开始右花括号结束:
if(test) {
    
    
	test = false;
	console.log(test)
}

在执行多条语句时必须有代码块,结构清晰,可以防止出错。(最佳实践)

// 有效 不推荐
if(test)
	console.log(test)

// 推荐
if(test) {
    
    
	console.log(test)
}

关键字、保留字

ECMA-262中规定保留一批关键字、未来保留字,这些关键字有特殊的用途

// 关键字
break		do			in				typeof
case		else		instanceof		var
catch		export		new				void
class		extends		return			while
const		finally		super			with
continue	for			switch			yield
debugger	function	this
default		if			throw
delete		import		try	

// 未来保留字---这些虽然还不是关键字但是未来可能会成关键字所以不推荐使用成标识符
// 始终保留
enum
// 严格模式下保留
implements	package		public
interface	protected	static
let			provate
// 模块代码中保留
await

变量

JavaScript中变量的定义是松散的,意味着变量可以保存任意类型的数据。每个变量只不过是一个用于保存任意值的命名占位符。ES所有版本都可以使用的varES6以后可以使用letconst

var

var message

这段代码定义了一个变量message,可以用来保存任何类型的值,不初始化的情况下,变量会保存一个特殊值undefined

var message = "hi"

这段代码定义了一个变量message,同时进行了初始化对变量进行了赋值。这里可以对变量进行保存以及对变量进行更改。
var声明作用域
使用var操作符定义的变量会成为包含他函数的局部变量。

function test(){
    
    
	var message = "hi";    // 局部变量
}
test();
console.log(message);    // 出错!

反之,如果不使用操作符就会创建一个全局变量。

function test(){
    
    
	message = "hi";    // 局部变量
}
test();
console.log(message);    // "hi"

值得注意的是,在函数的局部定义一个全局变量会很难维护,也会造成麻烦。因为不知道是不是故意省略的var。
使用var定义多个变量时,可以在一条语句中用逗号分隔每一个变量以及可选的初始化:

var message = "hi",
	found = false,
	age = 29;
// 这里的定义只是为了方便阅读

var声明提升

function foo(){
    
    
	console.log(age);
	var age = 26;
}
foo();    // undefined

这里函数没有报错是因为JavaScript运行时把上述代码等价成以下代码:

function foo(){
    
    
	var age
	console.log(age);
	age = 26;
}
foo();    // undefined

这就是所谓的变量提升,也就是把所有变量声明提升到函数作用域顶部,同时返回多次使用var声明同一个变量也是没有问题的(值得注意的是变量提升的是变量,不是变量的值)

function foo(){
    
    
	var age = 16;
	var age = 26;
	var age = 36;
	console.log(age);
}
foo();    // 36

同时,全局下声明的变量会成为Windows的属性

  1. 声明作用域
  2. 声明提升
  3. 全局下使用var会成为顶级对象windows的属性

let

相对于var操作符varlet的作用差不多,比较明显的区别在于:

let 声明的是块作用域

if (true) {
     
     
    let age = 26;
    console.log(age);    // 26
}
console.log(age);    // ReferenceError:age 没有定义

var 声明的是函数作用域

if (true) {
     
     
    var name = "Matt";
    console.log(name);    // Matt
}
console.log(name);    // Matt

在这里age变量之所以不能再if块外部引用,是因为它的作用域仅限于该块内部。块作用域是函数作用域的子集,所以var的作用域限制同样适用于let

let也不允许同一个块作用域中出现冗余声明。这样也会导致报错:

var age;
var age;

let name;
let name;    // SyntaxError; 标识符name已经声明过了

对于嵌套使用的标识符不会报错,因为同一个块中没有重复声明:

var name = "Nicholas";
console.log(name);    // "Nicoholas"
if(true){
    
    
	var name = "Matt";
	console.log(name);    // "Matt"
}
let age = 30;
console.log(age);    // 30
if(){
    
    
	let age = 16;
	console.log(age);    // 16
}

这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在
暂时性死区
let声明的变量不会再作用域中提升。在let声明之前执行瞬间被称为"暂时性死区",在此阶段引用任何后面才声明的变量都会抛出ReferenceError

// name会被提升
console.log(name);    // undefined
var name = "Matt";
// age 不会被提升
console.log(age);    // ReferenceError :age
var age = 16;

全局声明
使用let声明的变量在全局下不会成为windows对象的属性,var

var name = "Matt";
console.log(window.name);    // "Matt"

let age = 26;
console.log(window.age);    // undefined

条件声明
在使用var时,由于声明会被提升,JS引擎会自动将多余的声明在作用域的顶部合并成一个声明。因为let的作用域是块,所以不会检查前面是否已经使用了let声明,同时也就不可能在没有声明的情况下声明它。

使用try/catch语句或typeof操作符也不能解决,因为条件块中let声明的作用域仅限于该块。

for循环中的let声明
let之前,for循环定义的变量会渗透循环体外部,而使用let这个问题就解决了。

for(var i = 0 ; i < 5; ++i){
    
    
	//循环逻辑
}
console.log(i);    // 5
for(let i = 0 ; i < 5; ++i){
    
    
	//循环逻辑
}
console.log(i);    // ReferenceError :i 没有定义
for(var i = 0 ; i < 5; ++i){
    
    
	setTimeout(()=>console.log(i),0)
}
console.log(i);    // 5,5,5,5,5

for(let i = 0 ; i < 5; ++i){
    
    
	setTimeout(()=>console.log(i),0)
}
console.log(i);    // 0,1,2,3,4

使用var声明变量常见的问题就是对迭代变量的奇特声明和修改,而使用let声明迭代变量时,JS引擎在后台会为每一个迭代循环声明一个新的迭代变量。每个setTimeout引用的都是不同的变量实例

const

const的行为于let基本一致,唯一重要的区别是用它声明变量时,必须初始化变量,且尝试修改const声明的变量会导致运行时错误

const age = 26;
age = 36;    // TypeError:给常量赋值

// const 也不允许重复声明
const name = "Matt";
const name = "Nicholas";    // SyntaxError

// const 声明的作用域也是块
const name = "Matt";
if(){
    
    
	const name = "Nicholas";
}
console.log(name);    // Matt

var、let、const区别

var:变量声明提升、作用域提升、
let:暂时性死区、块级作用域、不用初始化、
const:声明同时必须初始化、修改也会报错、

JavaScript数据类型

JavaScript中有种基本类型数据Undefined、Null、Boolean、Number、String、Symbol、Bigint以及一种引用数据类型Object

数据类型检测

因为JS的类型系统是松散的,所以需要一种手段来确定任意变量的数据类型

typeof

typeof 对于原始类型来说,除了 null 都可以显示正确的类型
typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型,所以想判断一个对象的正确类型,这时候可以考虑使用 instanceof

console.log(typeof 2);               // number
console.log(typeof true);            // boolean
console.log(typeof 'str');           // string
console.log(typeof []);              // object     []数组的数据类型在 typeof 中被解释为 object
console.log(typeof function(){
    
    });    // function
console.log(typeof {
    
    });              // object
console.log(typeof undefined);       // undefined
console.log(typeof null);            // object     null 的数据类型被 typeof 解释为 object

instanceof

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;
typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function类型以外,其他的也无法判断

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){
    
    } instanceof Function);       // true
console.log({
    
    } instanceof Object);                   // true    
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
// 我们也可以试着实现一下 instanceof
function _instanceof(left, right) {
    
    
    // 由于instance要检测的是某对象,需要有一个前置判断条件
    //基本数据类型直接返回false
    if(typeof left !== 'object' || left === null) return false;

    // 获得类型的原型
    let prototype = right.prototype
    // 获得对象的原型
    left = left.__proto__
    // 判断对象的类型是否等于类型的原型
    while (true) {
    
    
    	if (left === null)
    		return false
    	if (prototype === left)
    		return true
    	left = left.__proto__
    }
}
console.log('test', _instanceof(null, Array)) // false
console.log('test', _instanceof([], Array)) // true
console.log('test', _instanceof('', Array)) // false
console.log('test', _instanceof({
    
    }, Object)) // true

constructor

console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {
    
    }).constructor === Function); // true
console.log(({
    
    }).constructor === Object); // true
//如果我创建一个对象,更改它的原型,constructor就会变得不可靠了
function Fn(){
    
    };
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor===Fn);    // false
console.log(f.constructor===Array); // true 

Object.prototype.toString.call()

toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。

Object.prototype.toString({
    
    })       // "[object Object]"
Object.prototype.toString.call({
    
    })  // 同上结果,加上call也ok
Object.prototype.toString.call(1)    // "[object Number]"
Object.prototype.toString.call('1')  // "[object String]"
Object.prototype.toString.call(true)  // "[object Boolean]"
Object.prototype.toString.call(function(){
    
    })  // "[object Function]"
Object.prototype.toString.call(null)   //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g)    //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([])       //"[object Array]"
Object.prototype.toString.call(document)  //"[object HTMLDocument]"
Object.prototype.toString.call(window)   //"[object Window]"

// 从上面这段代码可以看出,Object.prototype.toString.call() 可以很好地判断引用类型,甚至可以把 document 和 window 都区分开来。
// 全局下判断数据类型
function getType(obj){
    
    
  let type  = typeof obj;
  // 先进行typeof判断,如果是基础数据类型,直接返回
  if (type !== "object") {
    
    
    return type;
  }
  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  
  // 注意正则中间有个空格
}

// 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?
getType([])     // "Array" typeof []是object,因此toString返回
getType('123')  // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null)   // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined)   // "undefined" typeof 直接返回
getType()            // "undefined" typeof 直接返回
getType(function(){
    
    }) // "function" typeof能判断,因此首字母小写
getType(/123/g)      //"RegExp" toString返回

Undefined

在数据类型Undefined中值只有一个 undefined

使用var let 定义一个变量但是没有初始化的时候那么这个变量的值是 undefined

var age;
let name;
console.log(age == undefined);    // true
console.log(name == undefined);    // true

注意:永远不用显示地给某个变量设置undefined值。undefined值主要用于比较,这个值的目的是为了正式明确空对象指针和未初始化变量的区别

未初始化和未定义的变量使用 typeof 去判定返回的值都是 undefined ,所以我们在定义变量的时候同时应该立即进行初始化,这样当一个变量返回undefined 的时候我们就可以很快的知道这个变量是未声明而不是声明了但是没有初始化

let message;

if(message){
     
     
    //这块不会执行
}

if(!message){
     
     
    //这块会执行
}

if(age){
     
     
    // 这块会报错
}

Null

在数据类型 Null 中值也只有一个 null

逻辑上来讲,null 值表示一个空指针对象,这也是 typeof 传入 null 会返回 object 的原因

let car = null;
console.log(typeof car);    // object

undefined 值是由 unll 派生出来的,因此 undefinedunll 在表面上相等。但是两者的用途不一样,对任何变量不必显示的将一个变量值设为 undefined,但是对一个变量如果不知道填充什么内容时,可以使用 null 来填充,这样可以保持null空对象指针的语义,并进一步与 undefined 区分开来

Boolean

在数据类型中 BooleanJavaScript 中使用最频繁的类型之一,它有两个值truefalse
关于 Boolean 我们应该知道不同类型跟布尔值之间的转换规则:

数据类型 转换为true的值 转换为false的值
Boolean true false
String 非空字符串 " " (空字符串)
Number 非零数值(包括无穷值) 0、NaN
Object 任意对象 null
Undefined N/A(不存在) undefined

之所以需要理解这些转换的内容,是因为在控制流语句中会自动执行其他类型到布尔类型的转换

let message = "hello world!";

if(message){
    
    
	console.log("value is true");
}

// 这里可以看到message是字符串,但是console.log()会输出 "value is true" 。
// 因为这里message会自动的转换为等价的 true

Number

关于 Number 数据类型首先确定它的写法,根据进制 Number 的写法可以如下:

// 最基本的数值字面量格式是十进制,直接写出来就可以
// 十进制。(不用前缀)
let num = 55;    // 整数
// 二进制。(前缀0b 或 0B ,逢二进一)
let num21 = 0b10;    // 2
let num22 = 0b100;    // 4
// 八进制。(前缀 0 ,逢八进一 )
let num31 = 070;    // 56
let num32 = 010;    // 56
// 十六进制。(前缀 0x 或 0X ,逢十六进一)
let num41 = 0xA;    // 10
let num42 = 0x1f;    // 31

注意:在严格模式下八进制是无效的,会导致JavaScript引擎抛出错误。同时八进制字面量第一个数字必须是0,然后是相应的八进制数字(0-7).如果字面量中包含的数字超过有效的范围,后面的序列就会成为十进制。
除此之外,使用八进制和十六进制格式创建的数值在所有的数学操作中都被视为十进制

let num1 = 070;    // 56
let num2 = 079;    // 79 9超过八进制的取值,所以num2以及下面的num3成10进制
let num3 = 08;    // 8

未完待续。。。

String

Symbol

Bigint

Object

猜你喜欢

转载自blog.csdn.net/WDJ_webDeveloper/article/details/134221787