9-JS的组成和变量
推荐书籍
《Javascript高级程序设计》
《ES6标准入门》 es6.ruanyifeng.com
《Javascript权威指南》
《你不知道的Javascript》上中下
JS做客户端语言
按照相关的JS语法,去操作页面中的元素,有时还要操作浏览器里面的一些功能
- ECMA Script3/5/6: JS的语法规范(变量、数据类型、操作语句等等)
如,使用var还是let - DOM(document object model):文档对象模型,提供一些JS的属性和方法,用来操作页面中的DOM元素
- BOM(browser object model):浏览器对象模型,提供一些JS的属性和方法,用来操作浏览器
把整个浏览器刷新,实现浏览器页面跳转,关掉浏览器当前窗口,关掉窗口前做相关提示,检查当前浏览器版本等
JS中的变量(variable)
变量:可变的量,在编程语言中,变量其实就是一个名字,用来存储和代表不同值的东西
10-创建变量的几种方式
创建变量的几种方式
// ES3、5
/* var没有变量的作用域,是JS设计早期的一个缺陷,在ES6中改掉了,一般用let定义变量,用const定义常量 */
var a = 12;
a=13;
console.log(a); // =>输出的是a代表的值13
// ES6
let b = 100;
b = 200;
const c = 1000;
c = 2000; // =>报错:const创建的变量,存储的值不能被修改,特殊变量,(可以理解为叫做常量)
本身具体的东西是真正的常量,而起的名字是变量,名字可以指向其他东西;const本身起的是名字
// 创建函数也相当于在创建变量
function fn() {} // =>相当于创建一个变量名叫fn,fn存的是整个函数
// 创建类也相当于创建变量
class A{} // =>创建一个变量名叫A,存的是整个类
// ES6的模块导入也可以创建变量
import B from './B.js'; // =>导入一个模块,创建一个变量名B,存储这个模块的一些信息
// Symbol创建唯一值
let n = Symbol(100);
let m = Symbol(100);
n == m // =>false
11-JS命名规范
JS命名规范
- 严格区分大小写
let Test = 100;
console.log(test); // =>无法输出
- 使用数字、字母、下划线、$,数字不能作为开头
let $box; // =>一般用JQuery获取的以$开头
let _box; // =>一般公共变量都是_开头,公共变量/全局变量(约定俗成,非官方规范)
let 1box; // =>不可以,但是可以写box1
- 使用驼峰命名法:首字母小写,其余每一个有意义单词的首字母都要大写(命名尽可能语义化明细,使用英文单词)
let studentInformation;
let studentInfo;
// 常用的缩写:add/insert/create/new(新增)、update(修改)、delete/del/remove/rm(删除)、sel/select/query/get(查询)、info(信息)...
// 不正确的写法
let xueshengInfo;
let xueshengxinxi;
let xsxx;
- 不能使用关键字和保留字
当下有特殊含义的是关键字,未来可能会成为关键字的叫做保留字(?)
var let Cindy function …
var var = 10; //=>肯定不行的
// =>代码强迫症(代码洁癖):良好的编程习惯、极客精神
12-JS中的数据类型分类
JS中常用的数据类型
- 基本数据类型
- 数字number
常规数字和NaN - 字符串string
所有用单引号、双引号、反引号(撇)包起来的都是字符串
反引号包起来的是ES6新加的模板字符串
如 '{name:10}' 是字符串 - 布尔boolean
true/false - 空对象指针null
- 未定义undefined
- 数字number
- 引用数据类型
- 对象数据类型object
- {} 普通对象
- [] 数组对象
- /^[+-]?(\d|([1-9]\d+))(.\d+)?$/ 正则对象
- Math数学函数对象
- 日期对象
- ...
- 函数数据类型function
- 对象数据类型object
13-NUMBER数字数据类型详解
number数字类型
包含:常规数字、NaN(不是一个有效数字,但它率属于数字类型)
NaN
not a number:不是一个数,但它率属于数字类型
NaN和任何值(包括自己)都不相等:NaN!=NaN,所以我们不能用相等的方式判断是否为有效数字
isNaN
检测一个值是否为非有效数字,如果不是有效数字返回true,反之是有效数字返回false
// isNaN([val]) []参数描述占位符
console.log(isNaN(10)); // =>false
console.log(isNaN('AA')); // =>true
/*
* Number('AA') =>NaN 不能转化为数字
* isNaN('AA') =>true
*/
console.log(isNaN('10')); // =>false 是有效数字
/*
* Number('10') =>10
* isNaN(10) => false
*/
isNaN底层机制:在使用isNaN进行检测的时候,首先会验证检测的值是否为number类型,如果不是,先基于Number()这个方法,把值转换为数字类型,然后再进行检测
把其他类型值转换为数字类型
-
Number([val])
把字符串转换为数字,只要字符串中包含任意一个非有效数字字符(第一个点除外),结果都是NaN。
1)空字符串(''里面什么也没有,没空格)会变为数字零;
2)布尔值true会变成1,false变成0;
3)空对象指针null会变成0,未定义undefined会变成NaN。
4)把引用数据类型(包括对象包括函数)转化为数字,是先把它基于toString方法转化为字符串,然后再转换为数字。
注:
普通对象、正则对象等结果都是NaN,只有数组的结果才可能是数字。一系列转换是走的是浏览器底层渲染规则
console.log(Number('12.5')); // =>12.5 console.log(Number('12.5px')); // =>NaN console.log(Number('12.5.5')); // =>NaN console.log(Number('')); // =>0 console.log(Number(true)); // =>1 // 布尔转换为数字 console.log(Number(false)); // =>0 console.log(Number(false)); // =>0 console.log(isNaN(false)); // =>false console.log(isNaN(true)); // =>false // null->0 undefined->NaN console.log(Number(null)); //=>0 可以这样理解:空对象指针虽然代表没有,但其实还是有一个东西的 console.log(Number(undefined)); //=>NaN 可以这样理解:undefined代表未定义,未定义代表没有赋值,没有赋值的值就是没有值,没有值的话肯定是NaN // 把引用数据类型(包括对象包括函数)转化为数字,是先把他基于toString方法转化为字符串,然后再转换为数字 console.log(Number({name:'10'})); // =>NaN console.log(Number({})); // =>NaN // 普通对象 {}或{xxx:'xxx'}.toString() =>"[object object]" console.log(Number([])); // =>0 // [].toString -> "" console.log(Number([12])); // =>12 // [12].toString() -> "12" console.log(Number([12,23])); // -> NaN // [12,23].toString() -> "12,23"
-
parseInt/parseFloat([val],[进制]):也是转换数字的方法,对于字符串来说,它是从左到右依次查找有效数字字符,直到遇到非有效数字字符,停止查找(不管后面是否还有数字,都不再找了),把找到的当做数字返回
let str = '12.5px'; console.log(Number(str)); // =>NaN console.log(parseInt(str)) // =>12 console.log(parseFloat(str)) // =>12.5 console.log(parseFloat('width:12.5px')) // =>NaN parseInt("") // =>NaN (因为没有找到有效数字,如果是Number()方法则会返回0)
注:
Number(),parseInt/parseFloat()都是把其他类型转换为数字类型:
1)Number()是浏览器内置的一个非常重要的方法,它的一系列的转换规则,走的是浏览器渲染的底层规则,走的是V8引擎最底层的机制;
2)parseInt/parseFloat()是额外提供的一种方法,是处理这种字符串的,如果不是字符串,把它变成字符串,然后再从左到右查找,只能把字符串从左到右查找。
parseFloat(true); // => NaN
parseFloat('true'); // => NaN
- ==进行比较的时候,可能要出现把其它类型转换为数字
'10'==10 // =>true
- 数学运算
加减乘除,除了加可能变成字符串拼接
15-string字符串数据类型详解
string字符串数据类型
所有用单引号、双引号、反引号(撇 ES6模板字符串)包起来的都是字符串
把其它类型值转换为字符串
- [val].toString()
- 字符串拼接
console.log(12.toString()) // =>报错 实例概念
let a = 12;
console.log(a.toString()); // => '12'
console.log((NaN).toString()); // => 'NaN' 之前长什么样,外面包一层引号
// null和undefined是禁止直接toString 的
(null).toString() // =>报错 浏览器的保护机制
// 但是和undefined一样转换为字符串的结果就是'null'/'undefined '
[].toString(); // => ""
[12].toString(); // => "12"
[12,23].toString(); // => "12,23"
/^$/.toString(); // => "/^$/"
{{name: 'xxx'}}.toString(); //=> "[object Object]"
// 普通对象.toString()的结果是"[object Object]"
=>Object.prototype.toString方法不是转换为字符串的,而是用来检测数据类型的
// ============字符串拼接
// 四则运算法则,除加法之外,其余都是数学计算,只有加法可能存在字符串拼接(一旦遇到字符串,则不是数学运算,而是字符串拼接)
console.log('10'+10); //=>1010
console.log('10'-10); //=>0
console.log('10px'-10); //=>NaN
// 10px用Number()转换,NaN-10是NaN
let a = 10 + null + true + [] + undefined + '珠峰' + null + [] +10 + false;
console.log (a);
/*
* 10 + null -> 10 + 0 -> 10
* 10 + true -> 10 + 1 -> 11
* 11 + [] -> 11 + '' -> '11' 空数组变为数字,先要经历变为空字符串,遇到字符串,啥都别想了,直接变为字符串拼接
* '11'+undefined -> '11undefined'
* …
* '11undefined珠峰null10false'
*/
16-boolean布尔数据类型详解
boolean布尔数据类型
只有两个值 true/false
把其它类型值转换为布尔类型
只有0、NaN、''(空字符串)、null、undefined 五个值转换为false,其余都转换为true(而且没有任何的特殊情况)
V8引擎底层机制,在实现数据类型转换的时候,给予的机制就是这样的
- Boolean([val])
// 只要不在五种情况之中,都为true
console.log(Boolean('')); // =>false
console.log(Boolean(' ')); // =>true 有空格
console.log(Boolean([])); // =>true 五种之外
console.log(Boolean([12])); // =>true
console.log(Boolean(-1)); // =>true
- !/!!
!:取反(先转为布尔,然后取反)
!!:取反再取反,只相当于转换为布尔<=>Boolean
console.log(!1); //=>false
console.log(!!1); //=>true
- 条件判断
如果条件只是一个值,不是==/===/!=/>=等这些比较,是要把这个值先转换为布尔类型,然后验证真假
// 能输出 哈哈
if(1) {
console.log('哈哈');
}
// 能输出 呵呵
if('3px'+3) {
// => '3px3'
console.log('呵呵');
}
// 不能输出 嘿嘿
if('3px'-3) {
// => NaN-3 => NaN
console.log('嘿嘿');
}
17-null和undefined的区别
null/undefined
null和undefined都代表没有
- null:意料之中(一般都是开始不知道值,我们手动先设置为null,后期再给予赋值操作)
let num = null; // =>一般最好用null作为初始的空值,因为零不是空值,它在栈内存中有自己的存储空间(占了位置)
let num = 0; // =>有空间来存储,相对消耗一丢丢丢丢性能
...
num = 12;
- undefined:意料之外(不是我能决定的)
let num; // =>创建一个变量没有赋值,默认值是undefined
...
num = 12;
18-对象数据类型的基本结构和操作
object对象数据类型-普通对象
{[key]:[value],...}
任何一个对象都是由零到多组键值对(属性名:属性值)组成的,每组键值对或每组属性名和属性值之间用逗号分隔;
属性名不能重复;
属性名只能是数字或字符串;
属性名是数字或者字符串格式的,不写单引号双引号默认也是字符串格式的,加上也不会有错误,默认不写
let person = {
name: '易烊千玺',
age: 40,
height: '185cm',
weight: '80kg',
1: 100
};
获取属性名对应的属性值(查)
- 对象.属性名
属性名是字符串的情况可用 - 对象[属性名]
属性名是数字或者字符串格式(带引号)的
console.log(person.name);
console.log(person['age']); // 不能直接写age,属性名是个字符串
或console.log(person["age"]);
// =>如果当前属性名不存在,默认的属性值是undefined
console.log(person.sex); // =>undefined
console.log(person[1]);
// =>如果属性名是数字,则不能使用点的方式获取属性值
// console.log(person.1); // =>SyntaxError:语法错误
设置属性名属性值(改、增)
person.GF = '园园';
console.log(person['GF']); // =>园园
// =>属性名不能重复,如果属性名已经存在,不属于新增属于修改属性值
person.name = '李易峰';
console.log(person['name']); // =>李易峰
删除属性(删)
- 真删除:把属性彻底干掉
- 假删除:属性还在,值为空
// 假删除
person.weight = null;
console.log(person);
/*
* 1: 100
* age: 40
* height: "185cm"
* name: "易烊千玺"
* weight: null
*/
// 真删除
delete person[1];
console.log(person);
/*
* age: 40
* height: "185cm"
* name: "易烊千玺"
* weight: null
*/
19-数组的基本结构(特殊对象类型)
对象数据类型object-数组对象
数组是特殊的对象数据类型
1)我们中括号中设置的是属性值,它的属性名是默认生成的数字,从0开始递增,而且这个数字代表每一项的位置,我们把其称为“索引”=>从零开始连续递增,代表每一项位置的数字属性名
2)天生默认一个属性名length,存储数组的长度
let ary = [12,'哈哈',true,13];
// 任何一个对象都必须有键值对(看到的是属性值:属性名),数组也是对象,它肯定也是由属性名属性值组成的,只不过比较特殊,写的时候不需要写属性名,从0开始逐级递增,既是属性名也是索引
console.log(ary);
/* [12, "哈哈", true, 13]
* 0: 12
* 1: "哈哈"
* 2: true
* 3: 13
* length: 4
*/
// 默认一个属性名length
console.log(ary.length); // =>4
console.log(ary['length']); // =>4 需要写单引号 字符串格式
/* 数组虽然是特殊对象,看起来不一样,本质仍是对象,只要是对象,就可以使用上面的对象操作法则 */
// 输出哈哈 哈哈索引是1
console.log(ary[1]); // =>哈哈 属性名是数字只能用中括号方式
// 第一项索引是0 最后一项索引ary.length-1
console.log(ary[0]); //=>12
console.log(ary[ary.length-1]); //=>13
// 向数组末尾追加内容
ary[ary.length] = 100;
console.log(ary);
20-数据类型的区别(堆栈底层机制)
let a = 12;
let b = a;
b = 13;
console.log(a); // =>12
let n = {
name: '珠峰'
};
let m = n;
m.name = '培训';
console.log(n.name); // =>培训
浏览器想要执行JS代码:
环境、人
1)从电脑内存当中分配出一块内存,用来执行代码(栈内存=>Stack)
电脑买高内存的原因之一
2)分配一个主线程用来自上而下执行JS代码
上面用来存东西,下面用来执行代码
简单的基本类型值存储
- let a =12;
1.创建变量a,放到当前栈内存变量存储区域中
2.创建一个值12,把它存储到当前栈内存值区域中(简单的基本类型值是这样存储的,复杂的引用类型值不是这样做的)
3.=为赋值,其实赋值是让变量和值相互关联的过程 - let b = a;
上述第1条第3条 - b = 12;
上述第2条第3条
复杂的引用类型值存储
name属性名已存在,属于改
复杂值(引用类型值)的存储,又分成了三个步骤:
1.在内存中分配出一块新内存,用来存储引用类型值(堆内存=>heap ) =>内存有一个16进制地址
2.把对象中的键值对(属性名:属性值)依次存储到堆内存中
3.把堆内存地址和变量关联起来
注意:本质仍是遵照创建变量、创建值、关联,三个步骤(见下面题)
基本数据类型、引用数据类型存储对比
基本类型∶按值操作(直接操作的是值),所以也叫作值类型
引用类型∶操作的是堆内存的地址(按引用地址操作的),所以叫作引用数据类型
- 基本类型值
因为结构简单,直接存储在栈内存的值存储空间当中,所有的操作是直接操作这个值 - 引用类型值
结构复杂,不能直接存储在值存储空间中,必须开一个堆内存,将内容存在堆里面,然后操作的都是堆内存的引用地址,可能会导致两个变量用的都是同一个地址,管控的是同一个堆,其中一个变量把堆里的东西改了,另外一个变量受影响
21-堆栈内存课程练习题
最底层的机制
1、创建变量(先处理等号左边)
2、创建值(再处理等号右边)
3、关联,关联前两者无关(js的特点,不同于某些语言,不是拷贝,是关联)
x = [30,40]
创建过变量x了不用管,创建新的值-新数组-新空间,关联,x只能关联一个
22-阿里的一道引发血案的面试题
// 写出下面结果输出的答案
let a = {
n:1
};
let b = a;
a.x = a = {
n:2
};
console.log(a.x);
console.log(b);
关于连等赋值:
a=b=3
从左到右
a=3
b=3
3都是一个值3,先让a和它关联,再让b和它关联,连等赋值,是让每一个变量都和这个值关联
a.x = a = {n:2}
1、先处理等号左边的,看是不是要创建变量:
a.x 不是创建变量,是给a设置一个属性x
a变量已存在,不用创建
2、再处理等号右边的,看要不要创建值:
等号右边是个值,需要创建值,值存储空间/开个堆
等号右边是引用类型,开个堆,AAAFFF111
3、关联
先让a.x和值关联,之前没有x这个属性名,新增,x和AAAFFF111关联
再让a和AAAFFF111关联
// 变形
let a = {
n:1
};
let b = a;
a.x = b;
无限次嵌套,堆的嵌套,内存嵌套,导致内存无限溢出
23-数据类型检测
JS中的数据类型检测
- typeof [val]:用来检测数据类型的运算符
- instanceof:用来检测当前实例是否率属于某个类
- constructor:基于构造函数检测数据类型(也是基于类的方式)
- Object.prototype.toString.call():检测数据类型最好的办法
注:
1、前三个都有不足,第四个没有,做轻量级检测的时候,前三个能处理某些事,就不用第四个了
2、
typeof ,运算符,不是方法;
instanceof,运算符,不是方法;
constructor,属性,不是方法,但是constructor构造函数也可以当作方法;
运算符如 1 + 1中的+
方法如sum()
所以typeof运算符后面直接加value值,不加()执行
typeof
基于typeof检测出来的结果
1、首先是一个字符串
2、字符串中包含对应的类型
局限性
1、typeof null => "object" 但是null并不是对象
2、基于typeof无法细分出当前值是普通对象还是数组对象等,因为只要是对象数据类型,返回的结果都是"object"
console.log(typeof 1); // =>number (控制台打出来的是黑色字体的number,默认省略"",但控制台其实是有颜色区分的,黑色代表是字符串,蓝色是数字,直接在控制台typeof 1更加明显,是"number")
let a = NaN;
console.log(typeof a); //=>'number'
console.log(1);
console.log(typeof typeof typeof []);
// 从和值最近的开始,先靠近值的先运算,从右到左
// =>typeof [] => "object"
// =>typeof "object" => "string"
// 因为typeof检测的结果是字符串,只要两个及两个以上同时检测,最后结果必然是"string"
答案是"string" 加双引号或单引号,注意细节