JavaScript 开始部分

    在开始学习JavaScript时,一些书目介绍这是一门和C较为相近的语言,之前刚刚学习完C++的基础部分,所以在之后的学习中,我将尽量多的使用一些对比的方法,帮助学习和理解。

变量和数据类型

    变量方面,JavaScript是松散类型的,也就是一个变量可以保存任意类型的数据!这不仅仅在于一个数字类型的数据可以保存包括像C语言中int、short等整型变量以及float、double等浮点类型变量;而且可以在定义一种类型后,用另外一种类型的值为其赋值,这也是合法的!
    在定义变量时,虽然JavaScript内部将类型分为了基本的六种(之后会介绍)但类型名称却只有一种,即变量定义关键字 var (variable)。

var message = "hi"
message = 59;
就像上文提到的,这种之后的赋值是合法的
(但一般我们尽量避免这种改变变量类型的赋值)

var作用域

    在使用var进行变量定义时,定义的变量都是局部变量,当不使用var关键字进行定义时,得到的是全局变量。

function test(){
	message = "hi";//将var关键字省略从而定义一个全局变量。
}
test();
console.log(message);//hi将会被打印出
就好像C语言中未定义就直接使用一个变量进行赋值,可能较为难以接受,但是想到Python也是
这样操作的,理解起来就容易的多!
这也越来越让你感觉到C语言以及C++的规范性。

var声明提升

    对于函数中使用var定义的变量,都会等价的被视为是在函数体最顶端进行的定义,如下:

function foo()
{
	console.log(any_num);
	var num = 10;
}
之所以不会报错,是因为ECMAScript将以上代码自动认为是如下的代码:
function foo()
{
	var num;
	console.log(any_num);
	num = 10;
}
另外,多次声明同一个变量也是允许的
function ack()
{
	var age = 91;
	var age = 22;
	var age = 23;
	console.log(age);
}

let声明(对比var和let)

块作用域以及函数作用域

    letvar的作用差不多,但let声明的是范围块作用域,而var声明的范围是函数作用域。

if(true)
{
	var name = "CTR_Temp";
	console.log(name);//CTR_Temp
}
console.log(name);//CTR_Temp
if(true)
{
	let name = "CTR_Temp";
	console.log(name);//CTR_Temp
}
console.log(name);//Error 未定义报错

重复声明

var name = "Ctr";
var name = "SSS";//允许
并且,在同一个程序块中,let不允许以下的重复冗余定义
let ctr = 'temp';
let ctr = 'sss';//报错

暂时性死区(声明提升与否)

console.log(ctr)//sss
var ctr = 'sss';//声明提升

console.log(name)//报错:未定义
let name = 'sss';//不会声明提升

全局声明

在全局作用域中的var关键字定义的变量会被默认成为window对象的属性,但let不会
var ctr = 'sss';//声明提升
console.log(window.ctr)//sss

let name = 'sss';//不会声明提升
console.log(window.name)//报错:未定义

条件声明

    没接触过,暂时不做解释,看到介绍补充

for循环中的let声明

    其实在以上块作用域中已经进行了较为详尽的说明,我以下举一个不太清楚的例子:

for(var i=0; i<5; ++i){setTimeout(() => consloe.log())};
以上句子会打印 5,5,5,5,5

for(let i=0; i<5; ++i){setTimeout(() => consloe.log())};
以上句子会打印 0,1,2,3,4

const 声明

    const的行为与let基本相同,但使用const声明一个变量时必须同时初始化这个变量,且视图修改const声明的变量都会导致运行错误

const的限制值适用于它指向变量的引用,即允许你修改这个变量的内部属性
const person = {};//空对象?
person.name = 'ctr';//这是允许的

note!!!

    注意:一般来说,我们应该尽量避免使用var,转向使用letconst,这看起来与C/C++更为类似(强调块作用域以及没有声明提升),确实更加方便我们的理解(或许保留var就是为了与上一个版本的兼容)
    对于constlet,我们更倾向于优先使用const,只在确认变量在之后一定会被改动时使用let,这有助于之后的问题排查。

基本变量类型

    注意:JavaScript无法像C或C++一样自定义结构体或类,即无自定义类型,只有基本类型
    由于变量只用var来定义,我们只能通过初始化时的操作来推断它属于什么基本类型,但有些情况初始化往往对我们是不可见的,故常常使用typeof操作符来获取变量的基本类型。注意typeof是操作符,就像C++中的sizeof,之后的括号是可以省略的,但建议加上以增加程序的可读性。

像以下这样,通过初始化时候的赋值(其实不应该叫做赋值),自动地推断基本类型,
但注意,之后的赋值可能改变这些变量的值(但一般我们不推荐改变类型的赋值)。
let message = "hi",
	found = false,
	age = 99;

    如下对基本变量类型进行展示

let message;
typeof(message); 类型 undefined
typeof("sss"); 类型 string
typeof(23); 类型 number
typeof(null); 类型 object
typeof(true); 类型 boolean
typeof(test()); 类型 function

Undefined

    对于未被初始化的变量,无法对它执行任何操作,知道其被第一次赋值(初始化)使其拥有隐式的数据类型。

var message;
alert(message);//undefined
alert(none);//直接程序报错
alert(typeof(message));//undefined
alert(typeof(none));//undefined
最后这个,对于未声明的变量,执行typeof操作它也会返回undefined!

note!!!:由于未初始化变量和未定义变量调用typeof操作符都会返回undefined,故我们建议在声明变量的时候对其进行初始化,这样在typeof返回undefined时,我们就可以清楚的知道是变量未定义而非没有初始化。

Null

    Null也是一个数据类型,不过对它执行typeof操作会返回的是object类型。它的使用方法是:当你定义了一个变量准备用于保存之后的产生或定义的对象,那么最好先将这个变量初始化为null而不是其他值,这样只需检测null值就可以知道相应的变量是否已经保存了一个对象的引用

if(sss!=null)
{
	//执行XXX操作
} 
alert(null == undefined);//
注意这条语句将始终返回true

Boolean

    这部分主要是需要注意一些隐式的类型转换,其他类型如何隐式的被当做boolean类型中的一种true/false(映射关系)。

			 	true			false
String --> 	 非空字符串		  ""(空字符串)
Number --> 	 非零数字值		0NaN(非数值)
Object --> 	  任何对象			 null
Undefined -->  不适用		   undefined  

Number

    Number类型关于十进制、八进制、十六进制的表示与C语言是相同的,默认是十进制,进行计算时,内部会先都转化成十进制进行计算。
    数值是有最大值和最小值的限制的(不必记住是多少,总之很大),超出范围的数会被转化成Infinity值,分为+Infinity(记作Infinity)正无穷和-Infinity负无穷。通过使用isFinite()函数进行对数值的判断(是否超范围)。

    关于浮点数,需要注意的主要是两点:科学计数法舍进错误

var floatNum1 = 12.2;
var floatNum2 = 0.3;
var floatNum3 = .33;//这种表示同样有效,表示0.33,但是我们肯定是不推荐这样写的
var floatNum4 = 1.375e3;//实际上表示的是1375,这是科学计数法表示
var floatNum5 = 3.33e-2;//实际表示的是0.0333
浮点数占用空间是整数的两倍,所以一些情况看似是浮点数的数值会被解析为整数
var num1 = 10.0;//这样会被解析为整数而不是浮点数

    浮点数的最高精度为小数点后17位,但其进行算数计算时,精度远不如整数!!!比如:

var a = 0.2;
var b = 0.3;
var c = a + b;
if(c == 0.3)
	//...
按理说c=0.3无疑,但实际上c = 0.3000 0000 0000 00004,这是由于舍入误差所导致的,
这使得我们无法测试特定的浮点数的数值!故最好不要做上面的这种判断。

    特殊数值NaN,表示的意思是Not a Number,即非数字类型,在此简要介绍其几个特点:
1、任何涉及NaN的操作都会返回NaN。
2、NaN与任何值都不相等,包括它本身
    关于对非数值NaN的判断我们可以使用函数isNaN(),其中涉及一些类型转换,我们放在之后的类型转换专题进行介绍。

String

    关于String类型中的一些特殊字面值常量,这里不再一一列出,之后可能会有专题以供查阅,其实大部分和C++还是比较类似的。

使用单引号和双引号没有什么区别
var str1 = "sss";
var str2 = 'nnn';
但是要注意匹配的问题,前后引号类型要一致
var str_error = 'error";//报错
访问字符串长度:
var length = str.length;
字符串可以像C++中的string类型一样拼接,其操作是:先创建一个可以容纳拼接总长度字符的
空字符串,最后再将二者拼接后填充进去。
var str3 = "Java";
str3 = str3 + 'Script';
ECMAScript6新增模板字面量以及之后的相关

    见到,用到后进行详解

Object

    这个是一个比较陌生的变量类型,需要刻意理解一下。文中说:在ECMAScript标准中,Object是所有对象的基础,所以我认为在之后的学习中再去步步深入的去理解。(并且是因为这里刚刚开始,确实没有什么深入详细的介绍讲解)

最基本的对象创建
var obj1 = new Object();
var obj2 = new Object;//不传参,可以省略后面的括号,但是不建议这样做

Symbol——符号类型

    ECMAScript6新增数据类型!!!以下我们先来看看大致的使用方法。

let sys = Symbol();//对符号sys使用Symbol()函数进行初始化
上面这句话将sys特化为实例
console.log(typeof(sys));//symbol 

symbol:使用typeof操作符返回的“符号类型”
Symbol():用于创建新的符号的的函数(可以类比C++构造函数中的default构造函数,默认构造一个空类)
sys:符号实例
    为了方便理解,我们加入一段C++代码进行类比说明

class Sales_data {

public:
	Sales_data() = default;//自定义默认构造函数
	Sales_data(const string &s) :bookNo(s) {};//仅对string类型(非内置类型)进行初始化,其他默认
	Sales_data(const string &s, unsigned unit_num, double reve)://全体初始化
		bookNo(s), units_sold(unit_num), revenue(reve){};

private:
	/*...*/
};


Sales_data any_book = Sales_data();
cout << typeid(Sales_data).name() << endl;//class Sales_data

Sales_data:类类型名称,使用typeid(Sales_data).name()返回的类型(其实完整的返回是class Sales_data)。只不过这里是自定义的,上面的symbol是JavaScript中内含的基本类型。
Sales_data():用于创建特定类的函数(构造函数),且构造的类内属性都是默认值。
any_book:类(Sales_data)的实例

    对比C++,很大的不同之处在于Symbol定义的符号实例是特定的,唯一的,即使传入相同的参数,也不能判二者相等!

let gene_Symbol = Symbol();
let gene_ano_Symbol = Symbol();
console.log(gene_Symbol  == gene_ano_Symbol);//false

let name_Symbol = Symbol("CTR");
let name_ano_Symbol = Symbol("CTR");
console.log(name_Symbol == name_ano_Symbol );//false

    可以看到,通过Symbol定义的符号是唯一的标识符,即使是传入了相同的参数(或不传入),所声明的两个符号也不相同,Symbol()的作用就是用来创建唯一的记号。

对C++则不同,创建的两个类实例是可以判相等的(前提是你重载了这个==操作符)
test1 = Sales_data();
test2 = Sales_data();
cout << test1==test2 << endl;//true

    另外,Symbol() 函数不能与new关键字仪器作为构造函数使用,这是为了避免创建符号包装对象???(这里没有理解)。但像BooleanNumberString这样的类型都可以通过和new结合来支持构造函数,且可以用于初始化包含原始值的包装对象?!

let myBoolean = new Boolean();
let myString = new String();
let myNumber = new Number();
console.log(myBoolean);//object
console.log(myString);//object
console.log(myNumber);//object

let mySymbol =  new Symbol();//Error:Symbol is not a constructor
使用全局符号注册表!?

操作符

一元操作符

    和C++中相似,++操作和–操作,分为前置和后置。

var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2;//这里是用num1递减后的数操作赋值给num3
var num4 = num1-- + num2;//这里是用num1递减前的数操作赋值给num3

    另一种一元操作是一元+和-操作,类似于我们常说的正负号,用以转换属性。这部分的详细内容我们也放在之后类型转换的单独章节。

位操作符

    关于内存中的数据储存格式进行简要介绍。与C语言类似,带符号数31位表示数值大小,第32位表示数值符号:0代表正数,1代表负数。正数的储存格式是二进制原码、负数是二进制补码(想不起来了去复习数电)。
    一般我们定义的整数都默认为是有符号数。
    按位**与&、或|、非~、异或^**的操作与C相同,注意按位操作也包括符号位,不要忘记对符号位的操作。
    关于左移操作<< 和 右移>>操作: 注意左移,右侧补0;右移左侧补符号位(因为使用的是补码,故对于负数右移,左侧补充的都是1)。无符号右移>>> 操作左侧一律补0。

布尔操作符

    逻辑操作与&&、或||、非!,当然也涉及一些关于非boolean型变量使用这些操作符,这些应用将产生什么结果,我们也将放在之后的类型转化章节讨论。

数学运算操作符

    归为一个大类了,就是我们常常说的加减乘除运算所使用到的操作符。又分为乘性操作符,和加性操作符

乘性操作符

    说的就是 乘 、除 以及 求模,这里一些值得注意的地方就是对0和Infinity的操作。

±Infinity * 0 = NaN
±Infinity * num = ±Infinity 符号取决于num
Infinity * Infinity = Infinity

Infinity / Infinity  = NaN
0 / 0 = NaN
±Infinity / num = ±Infinity num≠0 符号取决于num

Infinity % num = NaN
Infinity % Infinity = NaN
num % 0 = NaN
num % Infinity = num
0 % any = 0

加性操作符

    加法和减法,最简单的操作符了吧!同样我们对Infinity和0相关的操作进行一下梳理。

NaN + any = NaN
Infinity + Infinity = Infinity
-Infinity + -Infinity = -Infinity
Infinity + -Infinity = NaN
+0 + +0 = +0
-0 + -0 = -0
+0 + -0 = +0

Infinity - Infinity = NaN
-Infinity - -Infinity = NaN
Infinity - -Infinity = Infinity
-Infinity - Infinity = -Infinity
+0 - +0 = +0
-0 - +0 = -0
-0 - -0 = +0

    同样的关于数值运算操作符相关的类型转换,也将在类型转换的独立章节进行展示。

关系操作符(比较操作符)

    主要需要注意一下关于非数值类型(Number)的比较(这里主要针对字符串的比较)。

比较的法则是从头到尾一个个的进行比较,以第一个不相等的字符为准则判断大小
字符的比较实际上是对编码的比较
var str1 = "Brick";
var str2 = "alpha";
alert(str1>str2);//false
由于'B'的编码小于'a',故被判断为false,实际上小写字母的编码都大于大写字母
如果想比较最好先都转换成小写字母
alert(str1.toLowerCase()>str2.toLowerCase());//true
另外对于任何数与NaN进行比较都返回false
alert(6>=NaN);
alert(6<NaN);//以上两个都返回false

    关于相等== 以及 **全等===**涉及类型转化,我们还是放到之后的独立blog。

条件操作符(也就是三目运算符)

    就是C中的三目运算符,不做过多介绍,大同小异。

赋值操作符

    最基本的 = 操作以及附带的一些操作包括 (+= *= <<= >>>=… …)和C类似,不做阐述。

逗号操作符

    用来解决一个语句执行多个操作。

最基本的如下,关于变量的定义
var num = 10, str = "sss", isM = "true";
需要特别注意的如下,形态上类似于C++的列表初始化,但实际很大区别
var num_X = (1,2,3,4,2,8);//最终的num被赋值为0,注意赋值操作符总会返回表达式中的最后一项

语句

    除了一些特有的语句,JavaScript中的语句使用方法和C++/C语言几乎都完全一致,后面提到的一些,详细可以参考我的C++语句部分的总结。

if语句

    使用方法和C语言完全一致,我们推荐使用代码块模式,即无论是否if条件内需要执行多少语句,我们都把它加上花括号。

do-while语句

    和C语言相同,语句块内部的语句会被执行至少一次,这也是和while语句不同之处(先执行后判断)

while语句

    先判断后执行,块内语句不一定被执行。

for语句

    与C语言相同,执行顺序也是一样的。
有一点与C语言不同需要注意,如下:

for(var i = 0; i<10; i++)
{
	//......
}
alert(i);//i = 10
由于ECMAScript标准中不存在 块级作用域(之后会详细讲,这里先了解)
故在for语句中定义的变量 i 在for执行完之后还是可以访问使用的。

for-in语句

    类似于Python中的in range和C++中的范围for 循环,可以精确的遍历一个枚举对象,如果要执行迭代的对象为null或undefined,则循环体中的语句将不会被执行,具体形式如下:

for(var propName in window)
{
	document.write(propName);
	//......
}

label语句(用到详细说明)

    类似于C++中的语句跳转?!

break和continue语句

    与C语言中的用法相同,最近迭代跳出/执行下一次!

with语句(用到详细说明)

    ???

switch语句

    同C语言用法一致,一些用法如合并执行等均可参考之前的C++总结。

函数

    ECMAScript标准中的函数基本定义形式如下:

function functionName(arg0, arg1, arg2,....,argN)//参数类型不必声明
{
	statements
}

    函数的返回值类型在函数定义时不必声明,任何函数都可以return一个值。return后函数终止执行。可以使用条件判断语句来选择性地返回多种值。

function sum(num1, num2)//参数名不允许同名
{
	return num1 + num2;
	//return之后的语句不会被继续执行了
}

对函数参数的理解

    ECMAScript标准对于函数定义和调用都是时十分松散的!怎么说呢?你定义时定义几个参数,但是调用时传入的参数可以与定义的参数数量完全无关,可以传入更多参数,可以更少,甚至没有。甚至函数定义时,没有参数,但是执行时却可以输入参数!
    主要是因为其传参机制如下:
1、参数可以理解为一个数列(数组)
2、调用时按次序依次从中取数执行,参数名字只是一个标识助记。

function sum(){
	return arguments[0] + arguments[1];
}//可以理解为,输入任意多个参数,我这个函数只负责将其前两个传入的参数相加

    其中的数列就是arguments,它是内置的,只要定义了函数就默认存在的。之后调用时多出来的变量当然是直接被舍弃掉了(因为函数内部根本没有用到,如果缺少传入参数,则会调用时,缺少的参数会被自动使用undefined代替。
    由以上的说明,我们不难理解以下两点:
**
1、函数的参数不能被命名为 arguments(其实还有eval也不行,现在先不详说)
2、函数无法通过参数来区分,故ECMAScript标准不支持函数重载
**

待解决问题

1、既然存在,如何定义和表示一个无符号数?(默认定义的是有符号数)
2、for循环中的let声明,第一个程序段,为什么打印出了5,5,5,5,5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值