JavaScript基础与高级
JavaScript基础
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
六种基本类型
- 字符串
- 数字
- 布尔值
- null
- undefined
- 对象
对象
JS中的变量都是保存到栈内存中的,基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量。
对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。
属性名简写
function makeUser(name, age) {
return {
name, // 与 name: name 相同
age, // 与 age: age 相同
// ...
};
}
检查属性是否存在
用 in 关键字
alert( "age" in user ); // true,user.age 存在
alert( "blabla" in user ); // false,user.blabla 不存在。
遍历属性和值
for (let key in user) {
// keys
alert( key ); // name, age, isAdmin
// 属性键的值
alert( user[key] ); // John, 30, true
}
我们可以用下面的方法访问属性:
点符号: obj.property。
方括号 obj[“property”],方括号允许从变量中获取键,例如 obj[varWithKey]。
其他操作:
删除属性:delete obj.prop。
检查是否存在给定键的属性:“key” in obj。
遍历对象:for(let key in obj) 循环。
我们在这一章学习的叫做“普通对象(plain object)”,或者就叫对象。
JavaScript 中还有很多其他类型的对象:
Array 用于存储有序数据集合,
Date 用于存储时间日期,
Error 用于存储错误信息。
……等等。
它们有着各自特别的特性,我们将在后面学习到。有时候大家会说“Array 类型”或“Date 类型”,但其实它们并不是自身所属的类型,而是属于一个对象类型即 “object”。它们以不同的方式对 “object” 做了一些扩展。
克隆对象
let user = {
name: "John",
age: 30
};
let clone = {}; // 新的空对象
// 将 user 中所有的属性拷贝到其中
for (let key in user) {
clone[key] = user[key];
}
克隆对象2
使用Object.assign
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// 将 permissions1 和 permissions2 中的所有属性都拷贝到 user 中
Object.assign(user, permissions1, permissions2);
// 现在 user = { name: "John", canView: true, canEdit: true }
浅拷贝和深拷贝
如果有一个对象如下:
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
现在这样拷贝 clone.sizes = user.sizes 已经不足够了,因为 user.sizes 是个对象,它会以引用形式被拷贝。因此 clone 和 user 会共用一个 sizes:
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true,同一个对象
// user 和 clone 分享同一个 sizes
user.sizes.width++; // 通过其中一个改变属性值
alert(clone.sizes.width); // 51,能从另外一个获取到变更后的结果
为了解决这个问题,并让 user 和 clone 成为两个真正独立的对象,我们应该使用一个拷贝循环来检查 user[key] 的每个值,如果它是一个对象,那么也复制它的结构。这就是所谓的“深拷贝”。
我们可以使用递归来实现它。或者为了不重复造轮子,采用现有的实现,例如 lodash 库的 _.cloneDeep(obj)。
创建常量对象属性
使用 const 声明的对象也是可以被修改的
通过引用对对象进行存储的一个重要的副作用是声明为 const 的对象 可以 被修改。
例如:
const user = {
name: “John”
};
user.name = “Pete”; // (*)
alert(user.name); // Pete
看起来 (*) 行的代码会触发一个错误,但实际并没有。user 的值是一个常量,它必须始终引用同一个对象,但该对象的属性可以被自由修改。
换句话说,只有当我们尝试将 user=… 作为一个整体进行赋值时,const user 才会报错。
也就是说,如果我们真的需要创建常量对象属性,也是可以的,但使用的是完全不同的方法。我们将在 属性标志和属性描述符 一章中学习它。
函数
函数创建的三种方式
//函数的三种创建方式
//第一种
function fun1(){
console.log('我是一个函数1')
}
//第二种
var fun2 =function(){
console.log('我是一个函数2')
}
//第三种(不怎么用)
var fun3 =new Function("console.log('我是一个函数3')")
立即执行函数
//立即执行函数
(function(){
alert('我是一个匿名函数,我想被立即执行');
})();
作为对象中方法简写
var obj={
fun:function(){
alert('我是一个方法')
}
}
var obj={
fun(){
alert('我是一个方法')
}
}
全局变量与局部变量
作用域
在JS中一共有两种作用域:
1.全局作用域
直接编写在script标签中的JS代码,都在全局作用域。
在全局作用域中:创建的变量都会作为window对象的属性保存;创建的函数都会作为window对象的方法保存。
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到。
2.函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁。
每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量。
当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用;如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错ReferenceError。
在函数中要访问全局变量可以使用window对象。
在函数中,不适用var声明的变量都会成为全局变量。
定义形参就相当于在函数作用域中声明了变量。
定义形参就相当于在函数作用域中声明了变量。
变量的提前声明
使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值);
但是如果声明变量时不使用var关键字,则变量不会被声明提前。
函数的声明提前
函数的声明提前:
使用函数声明形式创建的函数 function 函数(){},它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数;
使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。
this
构造函数
构造函数的执行流程:
- 立刻创建一个新的对象;
- 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象;
- 逐行执行函数中的代码;
- 将新建的对象作为返回值返回。
使用工厂方法创建对象【大批量的创建对象】
使用工厂方法创建的对象,使用的构造函数都是Object,所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象。
创建一个构造函数,专门用来创建Person对象的。
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是,构造函数习惯上首字母大写。
构造函数和普通函数的区别就是:调用方式的不同;普通函数是直接调用,而构造函数需要使用new关键字来调用。
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。
! 与Java不同,Java是先创建一个类,然后构造函数写在类里面,js是直接创建构造函数,然后演化出实例对象
工厂方法创建对象:因为每个函数里都是new Object(),所以返回的是Object。
构造函数是new 你自己定义的函数(),返回的名称也就是你自己定义的。
instanceof:检查一个对象是否是一个类的实例
使用instanceof可以检查一个对象是否是一个类的实例。
语法:
对象 instanceof 构造函数
如果是,则返回true,否则返回false。