前端开发【第4篇:JavaScript基础】

JavaScript简述

上一篇文章已经聊过JavaScript的历史了这里不再复述了直接切入正题,JavaScript是一门解释型、动态类型、弱类型语言。

解释型语言和编译型语言就类似看一本书,编译型语言就是直接把整本书给你翻译成中文让你看,效率高。解释型就是给你找一个翻译翻译一句你读一句相对编译型效率就底一些

动态类型语言:就是一个变量本身的类型不是在定义的时候就指定类型了,而是在在运行的时候动态赋值这个变量是什么类型的语言

弱类型语言与之对比的就是强类型语言拿Python举例:

# 定义一个变量
test_value = 1
# 我们赋值的时候已经指定这个变量为int类型我们可以使用进行int类型的操作
print(test_value + 1)
# 但是如果我们使用它进行字符串操作就会报错
print(test_value + "str_value")
"""
TypeError: unsupported operand type(s) for +: 'int' and 'str'
"""

强类型语言,现在如果你想用test_value进行字符串操作只能把它转为str类型

在看一下弱类型的代码JavaScript

let testValue = 1;
// 现在我们定义一个testValue为int类型并进行int
console.log(testValue + 1);
// 但是现下面的操作
console.log(testValue + "Hello World");
// 现在我们做字符串的拼接而这时候不需要转换,弱类型有时显得很方便,有时却又极易出错

哦~这里定义变量的时候ECMAScript6以后定义变量var将推出历史舞台改为:let 和const了

JavaScript存在方式

看一下HTML和JS的加载顺序

1.浏览器加载和渲染html的顺序

  1. 浏览器加载和渲染html的顺序
  2. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。
  3. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)
  4. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。
  5. 并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。阻塞加载
  6. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。
  7. JS、CSS中如有重定义,后定义函数将覆盖前定义函数

2. JS的加载

  1. 不能并行下载和解析(阻塞下载)
  2. 当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现.

3.如何加快HTML页面加载速度

  1. 页面减肥。页面的肥瘦是影响加载速度最重要的因素删除不必要的空格、注释。将inline的script和css移到外部文件,可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥
  2. 减少文件数量。减少页面上引用的文件数量可以减少HTTP连接数。许多JavaScript、CSS文件可以合并最好合并,人家财帮子都把自己的JavaScript. functions和Prototype.js合并到一个base.js文件里去了
  3. 减少域名查询。DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好
  4. 缓存重用数据。使用缓存吧
  5. 优化页面元素加载顺序。首先加载页面最初显示的内容和与之相关的JavaScript和CSS,然后加载DHTML相关的东西,像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载
  6. 减少inline JavaScript的数量。浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大,不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容
  7. 使用现代CSS和合法的标签。使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片,使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥
  8. Chunk your content。不要使用嵌套tables
  9. 指定图像和tables的大小。如果浏览器可以立即决定图像或tables的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作,这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。
  10. 根据用户浏览器明智的选择策略。IE、Firefox、Safari等等等等
  11. 页面结构的例子

4.HTML页面加载和解析流程

  1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
  2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
  3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
  4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
  5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
  6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
  7. 浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
  8. Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;
  9. 终于等到了</html>的到来,浏览器泪流满面……
  10. 等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;
  11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

5.Yahoo对网页设计性能的建议,个人感觉是说得非常好的。

英文版:http://developer.yahoo.com/performance/rules.html

中文翻译:http://www.cnblogs.com/smjack/archive/2009/02/24/1396895.html

原文链接:http://renyongjie668.blog.163.com/blog/static/1600531201097062789/

 

所以我们建议一般JavaScript代码放在Body最下面,且最好是通过外链式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--CSS样式建议放在head,外部调用-->
    <link type="text/css" rel="stylesheet" href="xiaomi_box/box_css.css">
</head>
<body>

    <!--HTML页面内容    -->

    <!-- 建议放在body最下面,外部调用-->
    <script type="application/javascript" src="4-1.js"></script>
</body>
</html>

JavaScript数据类型

JavaScript数据类型分为两类:原始类型(primitive type)和对象类型(object type)

原始类型:

数字、字符串、布尔值、还有两个特殊的原始值(null[空值]和undefined[未定义])

 

对象类型:

1、普通对象(集合-可以理解为字典且无序)

对象是属性的集合每个属性都是由"名/值组成"  可以理解为Python中的字典擦~   这个值可以是原始类型的数字、字符串也可以是对象

2、全局对象

有一个特殊的对象Global object

3、数组对象(有序的集合可以理解为列表)

数组(array),有序的集合可以理解为Python中的列表

4、函数对象

首先JavaScript里面没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。

重要的事情说三遍:JavaScript中没有类和实例的概念他是一个纯面向对象的语言,你可以理解为它的对象是靠一个对象来创建的

网景公司在发明与设计JavaScript的目标,其中很重要的两点:

1. 简易版的Java;

2. 简易,简易还是简易。

Brendan Eich设计JavaScript的时候引入了Java一个非常重要的概念:一切皆对象。既然JavaScript里面有了对象,那么设不设计继承就是困扰Brendan Eich的一个问题,如果真是要设计一个简易的语言其实可以不要继承机制,继承属于专业的程序员,但是JavaScript里那么多的对象,如果没有一种机制,他们之间将如何联系了,这必然会对编写程序的可靠性带来很大的问题,但是引入了继承又会使用JavaScript变成了完整的面向对象的语言,从而提高了它的门槛,让很多初学者望而却步,折中之下,Brendan Eich还是选择设计继承,但绝不是标准的继承(说道这里我想起了同样使用EMCAScript标准设计的语言ActionScript,它里面就有很完整的继承,做起来很惬意,我常想这是不是JavaScript以后的趋势,说不定哪天JavaScript会变的搄更完美写了?)。折中是指Brendan Eich不打算引入类(class),这样JavaScript至少看起来不像面向对象的语言了,那么初学者就不会望而却步了(这是欺骗啊,进来后倒腾死你,这就是所谓的关门打狗了,而且现在不还是引入了class吗,但是这个class实际还是调用了原型链)。

Brendan Eich思考之后,决定借鉴C++和java的new命令,将new命令引入了JavaScript,在传统的面向对象的语言里,new 用来构造实例对象,new 会调用构造函数,但是传统面向对象的语言new 后面的是类,内部机制是调用构造函数(constructor),而Brendan Eich简化了这个操作,在JavaScript里面,new 后面直接是构造函数,如是我们可以这么写一个Person类:

        function Person(name) {
            this.name = name
        }
        let personOne = new Person("Brendan Eich");
        console.log(personOne.name)

这样就创建了一个新的实例了。但是new有缺陷。用构造函数生成实例对象是无法无法共享属性和方法,例如下面代码:

        function Person(name) {
            this.name = name;
            this.nation = 'USA';
        }
        let person1 = new Person("Brendan Eich");
        let preson2 = new Person("Shuai Ge");

        person1.nation = "China";

        console.log(person1.nation);  // China
        console.log(preson2.nation);  // USA

每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。和JavaScript工厂模式的缺点一样,过多重复的对象会使得浏览器速度缓慢,造成资源的极大的浪费。

考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性,这个属性都是指向一个prototype对象。下面一句话很重要:所有实例对象需要共享的属性和方法,都放在这个Prototype对象(原型对象)里面;那些不需要共享的属性和方法,就放在构造函数里面

实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。如是我们可以改写下上面的程序:

        function Person(name) {
            this.name = name;
        }
        // 我们不需要指定prototype对象当我们创建对象的时候默认会生成
        Person.prototype = {nation: "USA"};

        let person1 = new Person("Brendan Eich");
        let person2 = new Person("Shuai Ge");
        
        console.log(person1.nation);
        console.log(person2.nation);

当我们这样写程序时候Person.prototype.nation = 'China'; 所有实例化的类的nation都会变成China。

由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。prototype只是提供了实现JavaScript继承的一个很方便的途径和手段。

5、日期、正则、错误 三种有用的对象

从字面上就可以看出日期是就代表日期的对象,正则表达式对象,还有就是定义了错误的对象

 

总结一下就是:数字、字符串、布尔值、null、undefined、对象(集合-字典)、数组(列表)、函数、日期、正则、错误这些对象类型,记住一切皆对象

 

 数字类型

1、JavaScript中的数字是不区分整数和浮点数的,它们默认都是采用浮点数展示

默认ES5是支持16进制的,但是不支持8进制,在ES6中明确了二进制采用[0b或0B],8机制采用[0o或0O]表示

2、数字的+、-、*、/、加减乘除

这些复杂的运算符都是通过Math对象属性定义的函数和常量实现的

3、二进制浮点数和四舍五入误差

首先要明确一点在计算机的世界里计算机只是别0,1,我们平时看到的任何 在计算机的理解力都是0,1,只是在我们看前做了一个转换,有了这个前提我们来看下

console.log(0.1 + 0.2 )
// 0.30000000000000004

what's FK ~ 什么鬼?JavaScript采用了IEEE-754表示法基本上现代编程语言都采用的是这个,你以为其他语言就会是对的吗?天真你试试~~

原因是:

那么0.1和0.2转换成二进制分别是,
 
(0.1) => 0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 101
(0.2) => 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 01
然后对上面的两个二进制数字做加法,得到的结果是,
 
0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101 01
再把这个二进制转换成十进制,就是我们前面的结果0.30000000000000004了

有写语言没有这个问题说明本质上对其做了封装~

ES6中也没有对其进行封装只提供了一个误差值~~!

那我们应该如何解决这个问题呢?两种方法:

变大求值

我们把float值放大N倍后为正数在进行计算

let a = 0.1;
let b = 0.2;
let ret = (a * 10 + b * 10) / 10;
console.log(ret);

字符串求值

let a = 0.1;
let b = 0.2;

let ret = (a + b).toFixed(1);
let newRet = parseFloat(ret);
console.log(newRet);

日期类型

JavaScript语言核心包括Date()构造函数,用来创建表示日期和时间对象。这些日期对想的方法为计算提供了简单的API

let beforDate = new Date(2011, 0, 1);  // 2011年1月1日
let laterDate = new Date(2011, 0, 1, 17, 10, 30);  //同一天,当地时间5:10:30pm
let nowDate = new Date();  // 当前日期和时间

let elapsed = nowDate - laterDate;  //日期减法: 计算时间检核的毫秒数

laterDate.getFullYear();  // ==> 2011 获取年份
laterDate.getMonth();  // ==> 0 获取从0计数的月份
laterDate.getDay();  // ==> 5 得到星期几,0代表星期日,5代表星期一
laterDate.getHours();  // ==> 17:5pm 当地时间
laterDate.getUTCHours();  // 使用UTC表示小时的时间,基于时区

还有很多方法可以查看文档

字符串

字符串和Python中的字符串差不多ES6中加强了Unicode表示法,只要将码点放入大括号,就能正确解读该字符。

"\u{20BB7}"
// "𠮷"

"\u{41}\u{42}\u{43}"
// "ABC"

let hello = 123;
hell\u{6F} // 123

'\u{1F680}' === '\uD83D\uDE80'
// true

JavaScript的字符串和Python中的字符串操作有很多相似之处也仅限于相似。

let s = 'hello world';  // 建议JavaScript字符串使用单引号因为在HTML中CSS样式类的应用是使用双引号的
s.charAt(0);            // ==> 'h' 第一个字符
s.charAt(s.length-1);   // ==> 'd' 最后一个字符
s.substring(1, 4);      // ==> 获取第1~4个字符类似列表切片
s.slice(1, 4);          // ==> 同上
s.slice(-3);            // ==> 'rld' 最后三个字符
s.indexOf('l');         // ==> '2' 获取字符第一次出现的位置
s.split(',');           // ==> 分割成数组
s.replace('h', 'H');    // ==> 'Hello World' 全文字符替换
s.toUpperCase();        // ==> 'HELLO WORLD' 转为为答谢

同样和Python中一样字符串是只读的如果想修改,只能新建一个新的

JavaScript可以理解为只读数组也可以用数组的部分功能比如s[0]下标~~

并且JavaScript本身支持正则匹配功能~

let text = 'testing: 1, 2, 3';  // 文本示例
let pattern  = /\d+/g;  // 匹配所有包含一个或多个数字的实例
pattern.test(text);  // 首先pattern定义了一个匹配方法,然后匹配的字符是text然后返回结果 基本上所有语言的匹配都大同小异

text.search(pattern);  // ==> 9 首次匹配成功的位置
text.match(pattern);   // ==> ["1", "2", "3"] 所有匹配组成的数组
text.replace(pattern, "#");  // ==> 'testing: #, #, #'
text.split(/\D+/);  // ==> ["", "1", "2", "3"] 用非数字字符截取字符串

 布尔值

True 或者False和Python中一样

null和undefind

null

JavaScript中的null它表示一个特殊值,常用来描述“空值”。null在javaScript中也是一个特殊的对象,它可以表示数字、字符串、对象是无值的

undefind

JavaScript还有第二个值来表示值的空缺,但是他表示更深层次的空值,表示变量未初始化或者未定义

 

undefind一般可以理解为出乎意料的的系统级别的空值,而null表示正常的意料中的空值,虽然它俩不同但是大多情况下可以互换的,但是在定义变量或者传参的时候建议使用null

 

全局对象

在原始类型和对象类型中,对象类型有个非常重要的对象:全局对象  全局对象定义的属性是全局定义的符号,JavaScript可以直接调用

 

JavaScript解释器启动的时候(或者页面浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性

  • 全局属性:比如undefind、Infinity、NaN
  • 全局函数:比如isNaN、parseInt()、eval()
  • 构造函数:比如Date()、RegExp()、String()、object()、和Array
  • 全局对象:比如Math和JSON

客户端window对象额外定义了一些全局对象,并且在代码最顶级-不在任何函数的JavaScript可以通过this关键字引用let global = this;

包装对象

JavaScript对象是一种复合值:他是属性或已命名值的集合通过 “.”【点】来调用属性值,当属性值是一个函数的时候称其为“方法”

我们来看下面一个例子:

let s = 'hello world';
s.substring(1,4)

在JavaScript中字符串不是一个对象那为什么他又属性和方法呢?

本质是:字符串在调用属性的时候会将字符串的值通过调用new string(s)的方式转换为对象,一旦引用结束就个被创建的对象就被销毁(其实在实现上并不一定销毁这个临时对象,整个过程看起来是这个样子)我的天啊~~

包括数字、和布尔值都是同理~  Number()或Boolean

"==" 等于运算将原始值和包装对象视为相等,单“===”全等运算将它们视为不等

不可变原始值和可变的对象引用

JavaScript中原始值(字符串、数字、null、undefind、布尔值)与对象(包括数组和函数)有本质的区别,原始值不可以修改比如9这个数你怎么改成其他值这本身就说不通9就是9他不能修改

 字符串就不太明显了,可以用Python的理解,同样不可以修改,字符串是一段连续的内存地址

 

对于对象来说他们是可变的可修改的,并且即使两个对象的属性及其相同的值他们也是不相等的,各索引元素完全相等的两个数组也不相同

let arrayA1 = [1, 2, 3, 4]
let arrayB1 = [1, 2, 3, 4]
arrayA1 == arrayB1
false

我擦这是什么鬼?这个我们可以理解为我们创建了两个对象在内存中,两个不同的内存地址怎么能相等呢?我觉的JavaScript在这里更精准些

我们通常将对象称为引用类型(reference type) 从此来和JavaScript的类型区分开,依照术语对象之都是引用,这个“引用可以理解为对内存地址的引用”

let arrayA = [];  //定义一个空的数组对象
let arrayB = arrayA;  // 把arrayA的值赋值为arrayB
arrayA[0] = 1;  //给arrayA里加一个元素因为A和B同样指向的是一块内存所以B也随之修改
arrayA === arrayB
true

上面是铁则化简就是:只有两个值引用的内存地址相同才相等

所以如果引用同一个内存地址那么一个值修改的时候另一个值将同样修改,有时候你只想复制一份可以通过循环来取

a = [1, 2, 3, 4, 5, 'hello']
b = []
for(let i = 0; i <a.length; i++){ b[i] = a[i]}

复制完也是不同的因为两个是不同的对象需要注意

类型转换

这个里面需要注意的是:

相等性:

null == undefind 是相等的  

"0" == 0 在比较之前将字符串转换为数字

0 == false  比较之前将布尔值转换为数字

“0” == false 在比较前将布尔值和字符串转化为数字

显示类型转换

除了布尔值和undefind之外的任何值都具有toString()方法这个方法通常和string()方法返回结果一致

// 二进制转换
let n = 17

binary_string = n.toString(2)  //转换为二进制
octal_string = "0" + n.toString(8) //转化为8进制
hex_string = "0x" + n.toString(16)  //转化为16进制

对象转换为原始值

({x: 1, x2: 2}).toString()
"[object Object]"
[1, 2, 3].toString()
"1,2,3"
(function(x){f(x);}).toString()
"function (x){f(x);}"
/\d+/g.toString()
"/\d+/g"
new Date(2017,9,12).toString()
"Thu Oct 12 2017 00:00:00 GMT+0800 (中国标准时间)"

这个结果很有意思~~

JavaScript对象到字符串转化的过程如下:

1、首先判断是否有toString()方法如果他返回一个原始值将这个值转化为字符串返回

2、如果没有toString()方法或者返回的不是一个原始值则调用则掉用valueOf()方法,如果存在这个方法则调用它,如果返回的是原始值则转化为字符串

3、否则javaScript无法从toString()或valueOf()方法中获得一个原始值则抛出异常

在对象到数字的过程中做了如下操作

1、如果对象存在valueOf()方法调用他如果返回一个原始值将这个原始值转换为数字(如果需要的话)然后返回数字

2、否则如果具有toString()方法,后者返回一个原始值JavaScript将它转换并返回

3、异常

对象转换为数字的细节解释了为什么 空数组会被转化为数字0数组默认继承了valueOf()这个方法返回一个对象在调用toString()方法转换为空字符,空字符在转化为数字,JavaScript “+”和“==”如果一个是对象JavaScript会将方法转化为原始值而不是其他素数运算符的方法执行对象到数字的转换

日期的情况比较特殊它是JavaScript语言核心唯一的预先定义类型,他定义了有意义的到字符串和数字的转换通过valueOf或toString()返回原始值将被直接使用而不是强制转换为数字或字符串

函数作用域及声明

let块级作用于

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

所以for循环很适合let

for (let i = 0; i < 10; i++) {
  // ...
}

console.log(i);
// ReferenceError: i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。

下面的代码如果使用var,最后输出的是10

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是6。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

不允许使用提前

在ES5我们调用1个变量的时候如果他没有声明默认是undefind,现在不可以了没有声明的变量不允许使用

暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

这里也以为typeof将不是一个安全操作因为它会触发异常了如果变量没有声明

不允许重复声明在相同作用于块内

function func(arg) {
  let arg; // 报错
}

function func(arg) {
  {
    let arg; // 不报错
  }
}

为什么要引入块级作用域?

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

const声明一个只读的常量。一旦声明,常量的值就不能改变。
这意味着这个常量一旦声明就必须立刻赋值以后不允许修改

ES6中有了块级作用域,JavaScript的作用域链基本上和Python的作用域相似了  全局和局部作用域

特殊的是块级作用于的暂时性死区

表达式和运算符

直接定义原始类型也就是直接声明 

let a = 1;
let b = "hello"
let c = null
let d;  // undefind

对象和数组的初始化表达式也就是直接声明对象和数组

let a = {}
let b =[]

函数定义表达式也就是声明一个函数

let function a { console.log("Hello World~ ")}

属性的访问

普通对象{} 可以通过.或者对象[‘key’]来进行访问,数组对象的话可以类似python的列表访问方式

如果属性对应的值是一个方法的话可以加()执行

对象创建表达式

let a = new Object()

加、减、乘、除、位运算

“==”和“===”相等和不等运算

== 和 ===  、   == 为宽松的判断是否相等, === 为严格的判断是否相等, === 在判断的时候不会做类型的转换!!!

他是如何严格判断的呢?

  • 如果两个值类型不相同则不相等
  • 如果两个值都是null或者都是undefind它们不相等
  • 如果两个值都是布尔值true或者false它们相等
  • 其中一个是NaN或者两个值都是NaN则它们不相等,NaN和其他任何值都不相等包括它本身,因为NaN本身是“无意义”
  • 如果两个值为数字且相等则它们相等,如果一个为0另一个为-0则也相等
  • 如果为字符串且对应16位数完全相等则它们相等
  • 如果两个引用值指向了同一个对象、数组、或函数则它们相等

不严格的==是如何判断的?

1、如果两个操作数类型相等,那么就类似严格相等

2.、如果操作两个数类型不同可能相等:

  • undefind和null相等字符串
  • 数字比较对边进行类型转换后进行比较
  • 如果其种一个值是true转换为1比较,如果其中一个是false转换为0比较
  • 如果一个是对象,另一个是字符串转换后比较
  • 其他类型之间比较均不相等

比较运算符类似==可能会进行类型转换

 

instancefo类似Python中的isinstance

let nowDate = new Date()
undefined
nowDate instanceof Date
true

Python的是

print(isinstance(11, int))

条件运算符JavaScript唯一一个三元运算符 ?

表达式: bool ? true: false   解释:问号前面为判断式,如果为真用 冒号前的true对应的表达式值计算然后返回,否则用冒号后面的false表达式计算并返回

// username需要提前定义ES6已经不允许未定义使用了
greeting = "hello" + ( username ? username : "there")
// 等价于

let greeting = "hello";

if (username){
    console.log(greeting + username);
}else{
    console.log(greeting + "there");
}

其他运算符

1、typeof获取类型

2、delete删除属性和python中的类似

3、逗号运算符哈哈

逗号运算符是二元运算符,它的操作数可以是任意类型。它首先计算左操作数,然后计算右操作数,最后返回右操作数的值,看下面的代码

i = 0, j = 1, k = 2
// 最后返回的结果是 2,它和线面的代码基本上是等价的
i = 0; j = 1; k =2

用在哪里?

// for循环中的第一个逗号是第let语句的一部分
// 第二个逗号是逗号运算符
// 它将两个表达式(i++和j--)放在一条for循环中
for(let i = 0, j = 10; i <j ; i++, j--){
    console.log(i + j)
}

 

posted @ 2017-09-12 16:34  天帅  阅读(902)  评论(0编辑  收藏  举报