总结js的变量这块的内容,从浅入深理解和正确使用JS的变量
目录
题一:
console.log('one',a);
var a = 1;
function a() {
console.log('two',2)
}
console.log('three',a);
var a = 3;
function a() {
console.log('four',a);
}
console.log('five',a);
a();
执行结果:
编译结果:
var a;
var a;
function a(){
console.log('two',2)
};
function a() {
console.log('four',a);
};
console.log('one',a);
a = 1;
console.log('three',a);
a = 3;
console.log('five',a);
a();
js会经过编译之后才会执行。执行阶段是从上到下执行。这就是变量提升
题二
console.log('one',a());
var a = 1;
function a() {
console.log('two',2)
}
console.log('three',a);
var a = 3;
function a() {
console.log('four',a);
}
console.log('five',a);
a();
结果:
编译结果:
var a;
var a;
function a(){
console.log('two',2)
};
function a() {
console.log('four',a);
};
console.log('one',a()); //注意 这里console出去的是a函数执行的结果 因为a函数没有return返回 所有是undefined
a = 1;
console.log('three',a);
a = 3;
console.log('five',a);
a();
题三:
console.log(a);
a = 1;
一、变量的命名规则
变量名的组成规则:
- 区分大小写。允许包含字母、数字、$、_ 但是第一个字符不允许是数字,不能出现空格或者.等标点符号
变量名的命名规范:
- 命名长度尽量要短,抓住重点。要提现出变量名中值得体现的类型
- 见名知义
- 禁止使用关键字
变量名的书写规范:
- 驼峰式:大驼峰每个单词首字母大写
- 小驼峰:第一个单词首字母小写,其他单词首字母大写
- 未知名字:全部使用小写,单词与单词用下划线链接
- 匈牙利命名法:类型+对象描述 Int整型(i)Float浮点(f)boolean布尔(b)string字符串(s)array数组(a)object对象(o)function函数(fn)Regular Expression正则(re)
二、声明变量的方法
声明方法:var let const class import function
隐式声明
- 不使用任何关键字来声明变量。变量会默认为全局变量,挂载在window下
var声明变量
- 声明变量的时候存在声明提升
- 声明可以重复被使用
- 变量可以重复被赋值
- 声明变量时,在代码块内可以修改代码块外声明的变量
- var声明的变量全局内有效(函数外使用)
- 不要把var 放在代码块中
- 不要把var放在循环体中
- 每个函数都使用单一的var语句
let声明变量
- 声明一个只有块级作用域的变量
- 不能与var操作同名
- let不允许在相同作用域内声明同一个变量
- for循环内适合使用let
- 作用域范围块级的
- 声明的变量只在其代码块内生效
- let不会发生变量提升
- 在代码块中声明变量不受该代码块之外的干扰,也不会影响代码块之外的同名变量
- 全局的let定义的变量不会被当做window属性
const声明变量
- 一个常量不能和它所在的作用域内的其他变量拥有相同的名称
- 不可重复声明
- 声明的是常量,一旦声明常量的值不能改变
- 只在声明的块级作用域内有效
- 声明的常量不会受到外层代码块的干扰,同样也不会感染外部变量和常量
- 一旦声明变量,必须立刻初始化,不能以后赋值
- 指向的是常量所在的地址
function声明函数
- 函数的提升优先于变量提升
- 对于var声明的变量如果未赋值,则function定义的重名变量会覆盖掉var的声明;而当var声明的变量已被赋值,则function定义的重名变量无效。
class声明类
关键字:class extends super constructor static
1.什么是class?Class 是ES6的语法。实际是对象继承的的语法糖
class SuperType{
constructor(name){
this.name = name;
}
SayName(){
console.log(this.name);
}
}
class可以看成构造函数的另外一种写法 constructor 就是构造方法 this关键字代表实例对象 SayName代表函数内部的方法
function SuperType(name) {
this.name = name;
}
SuperType.prototype = {
SayName:function () {
console.log(this.name);
},
}
调用方法都一样 使用new 操作符 class 会直接调用 constructor方法 返回对象的实例 直接调用class的方法会报错 除非显式的定义在this对象上。否则其他都在原型上 class 定义不存在变量提升 先使用在定义是错误的
2.extends 继承
通过extends 关键字实现继承,这比ES5通过修改原型链实现继承要清晰和方便
class SubType extends SuperType{
constructor(name,age){
super(name);
this.age = age;
}
}
super 表示 父类的构造函数 用来新建父类的this对象 即可以当函数使用 也可以当做对象使用 super作为函数调用时,代表父类的构造函数 super() 只能用在子类的构造函数中 子类中super.SayName() super指向父类的prototype
3.static 静态方法 所有在类中定义的方法都会实例继承。如果在方法前面 + static关键字 表示该方法不好被实例继承。直接通过类来调用 称为静态方法 父类的静态方法可以被子类继承
class SubType extends SuperType{
constructor(name,age){
super(name);
this.age = age;
}
static SayAge (){
console.log(this.age);
}
}
SubType.SayAge();
var a = new SubType();
a.SayAge();
4.Mixin模式 将多个类的接口混入 mix in 另一个类中
function mix(...mixins) {
class Mix {}
for (let mixin of mixins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if ( key !== "constructor"
&& key !== "prototype"
&& key !== "name"
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
//使用
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
import声明
用于导入另外一个模块的导出绑定
全部语法:
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
具体分析:
1.
import '/modules/my-module.js';
不导入任何模块,仅仅执行对应文件中的全局代码。一般用来指定某些补丁包
2.
import myDefault from "my-module";
直接导入默认值
3.
import * as myModule from '/modules/my-module.js';
这将myModule
插入当前作用域,其中包含来自位于/modules/my-module.js
文件中导出的所有模块
例如my-modules中有个doScroll方法,调用的时候需要 myModule.doScroll() 来执行
4.
import {myExport} from '/modules/my-module.js';
单个导入,将my-module.js模块下名为myExport的文件导出,并且将myExport插入当前作用域
5.
import {foo, bar} from '/modules/my-module.js';
同时导出多个对象或者值 foo和bar 其中foo 和bar 要在export中完全匹配
6.
import {reallyReallyLongModuleExportName as shortName} from '/modules/my-module.js';
导入时候重命名将reallyReallyLongModuleExportName导出并且重命名为shortName
7.
import {
reallyReallyLongModuleMemberName as shortName,
anotherLongModuleName as short
} from "my-module";
使用别名的多个模块导出
8.
import myDefault, * as myModule from "my-module";
将模块myDefault导出并且赋值给myModule变量
export全部语法:
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName() {...}
export class ClassName {...}
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
注意:命名导出(export)对导出多个值很有用。在导入(import)期间,必须使用相应对象的相同名称。可以使用任何名称导入默认导出
export default k = 12; // in file test.js
import m from './test' // note that we got the freedom to use import m instead of import k, because k was default export
console.log(m); // will log 12
多个export导出在不同环境展示不同的问题 babel 版本问题
三、变量的提升
变量的生命周期:
- 变量声明
- 变量初始化
- 变量使用
- 变量销毁
注意:const的声明和初始化是一起的。当变量被保留引用的时候,不会被销毁。
变量提升的原因:
- js代码运行之前有编译阶段,编译是从上到下执行的。编译完毕,才是执行阶段。js在代码编译阶段的主要工作:将变量和作用域关联。把函数和变量的声明提升至顶端。变量提升只提升声明。不提升赋值操作
变量的先提升,函数后提升。函数会覆盖同名的变量。函数的优先级较高
四、变量的作用域
全局作用域
- 在函数体外定义的变量
- 在函数体内隐式声明的变量
- window.标识符声明
- 可以在任何位置调用
局部作用域
- 函数内部使用var声明
- 函数的参数
- 只能在函数内部调用
块级作用域
- 块级作用域if
- 块级作用域for
优先级
- 局部变量高于同名的全局变量
- 参数变量高于同名的全局变量
- 局部变量高于同名的参数变量
特性
- 局部变量是调用对象的属性
- 全局变量是全局对象的属性
- 内层函数可以访问外层函数的局部变量
- 外层函数不能访问内层函数的局部变量
五、变量的数据类型
基本类型
- 占用空间固定保存在栈中
- 保存于与复制是值的本身
- 使用typeof检测数据类型
- 数据类型:string null boolean Number undefined
引用数据类型
- 占用空间不固定,保存在堆中
- 保存与复制是指向对象的一个指针
- 使用instanceof检测数据类型
- 使用new 方法构造出的对象是引用类型