文章目录
ES5的时候,我们知道的数据类型是 6种:number、 String 、Boolean 、undefind 、Null、object
ES6中新增了一种Symbol。这种类型的对象永不相等,即使创建的时候传入相同的值,可以解决属性名冲突的问题,作为标记
谷歌67版本中还出现了一种biglnt。是指安全储存、操作大整数。
所以JS的数据类型有几种?
8种。number、String、Boolean、Null、undefined、object、symbol、bigInt
js数据类型分为两类:基本数据类型和引用数据类型
基本数据类型:除Object。number、string、Boolean、Null、undefined
引用数据类型:就是Object。其中包含:function、Array、Date
了解完数据类型,我们来了解数据类型里面的对象
一.Object(对象)
1.对象使用
对象:用来储存键值对和更复杂的实体
创建空对象:
1.let obj = new Object();//构造函数语法创建对象
2.let obj = {};//‘字面量’语法创建对象
通常我们用花括号的这种种方式叫做字面量
let user = { // 一个对象
name: "John", // 键 "name",值 "John"
age: 30 // 键 "age",值 30
};
生成的 user 对象可以被想象为一个放置着两个标记有 “name” 和 “age” 的文件的柜子。
所以在这个柜子里我们可以随时添加、删除和读取文件。并且可以使用点符号访问属性值
如何访问属性值?
点符号: user.name。
方括号:user[“name”],方括号允许从变量中获取键
如何添加属性值?
user.sex = '女';
如何移除属性?用delete
delete user.age;
3.使用方括号,如果我们需要一些更复杂的内容,那么就用方括号。
2.属性存在性测试
属性是否存在对象中,用“in”操作符测试
let user = { name: "John", age: 30 };
alert( "age" in user ); // true,user.age 存在
alert( "blabla" in user ); // false,user.blabla 不存在。
in 的左边必须是 属性名。通常是一个带引号的字符串。
3.如何遍历对象
可以使用一个特殊形式的循环:for…in
for (key in object) {
// 对此对象属性中的每个键执行的代码
}
4.对象的引用和复制
实际上对象存放在栈中,里面的属性存放在堆内存中。
如果当一个对象变量被复制-引用被复制,而对象自身并没有被复制。
let user = { name: "John" };
let admin = user; // 复制引用
变量之间的赋值是在栈中,他们指向的都是同一个地址。这里仍然只有一个对象,但是有两个引用他们的变量
- n个引用变量指向同一对象,通过一个变量修改对象内部数据,其他所有变量看到的是修改之后的数据。因为两个变量指向同一个地址,我们通过其中一个变量改变地址中的内容,其他变量指向的内容也改变
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete';
console.log(user.name);//输出为Pete
user.name ='lili';
console.log(admin.name,user.name);//输出都为lili
这就像我们有一个带有两把钥匙的柜子,使用其中一把钥匙(admin)打开柜子并更改了里面的东西。那么,如果我们稍后用另一把钥匙(user),我们仍然可以打开同一个柜子并且可以访问更改的内容。
- 2个引用变量指向同一对象,让其中一个引用变量指向另一个对象,另一个变量依然指向前一个对象
let a = {age:12};
let b = a;
a= {name: 'BOB',age:13};
b.age = 14;
console.log(b.age,a.name,a.age);//14 BOB 13
5.垃圾回收
在js中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,垃圾回收是自动完成的,我们不能强制执行或者阻止。
我们需要做的只是将不再使用的对象设置null即可
6.对象方法this
-
为什么会有this?
当我们面向对象的时候,我们需要用不同的变量来访问同一个方法,this就可以充当任何一个变量来调用。 -
在JavaScript中this关键字与其他大多数编程语言不同,JavaScript 中的 this 可以用于任何函数,即使它不是对象的方法。
-
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象
1、如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
2、如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
3、如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
this的不同的情况:
- 以函数的形式调用时,this永远都是window
- 以方法的形式调用时,this就是调用方法的对象
- 以构造函数的形式调用时,this就是新创建的对象
- 使用call和apply调用时,this就是指定的那个对象
- 在全局作用域中this代表window
改变函数内部this指向:
JavaScript为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题。常用的有bind()、call()、apply()三种方法
二.构造函数
1.构造函数
构造函数,或简称构造器,就是常规函数,但大家对于构造器有个共同的约定,就是其命名首字母要大写。构造器的主要目的 —— 实现可重用的对象创建代码
构造函数:是一种特殊的函数,通过new函数名来实例化对象
主要作用:用来初始化对象,添加属性和方法
注意:1.首字母大写 2.只能由 “new” 操作符来执行。
new在执行会做四件事:
1.在内存中创建一个新的空对象
2.让this指向这个新对象
3.执行构造函数里面的代码,给这个新对象添加属性和方法
4.返回这个新对象(所以构造函数里面不需要return)
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
alert(user.name); // Jack
alert(user.isAdmin); // false
所以 new User(“Jack”) 的结果是相同的对象:
let user = {
name: "Jack",
isAdmin: false
};
构造函数中包含静态成员 和 实例成员:
1.实例成员就是构造函数内部通过this添加的成员 uname age sing就是实例成员
实例成员只能通过实例化的对象来访问 不能通过构造函数来访问
function Star(uname,age){
this.uname = uname;
this.age = age;
this.sing = function(){
console.log('我会唱歌');
}
}
var ldh = new Star('李德华',18);
2.静态成员 在构造函数本身上添加的成员 sex就是静态成员静态成员只能通过构造函数来访问
Star.sex = '男';
console.log(Star.sex);
2.构造器的return
构造器没有 return 语句。它们的任务是将所有必要的东西写入 this,并自动转换为结果。
但是,如果这有一个 return 语句,那么规则就简单了:
- 如果 return 返回的是一个对象,则返回这个对象,而不是 this。
- 如果 return 返回的是一个原始类型,则忽略。
换句话说,带有对象的 return 返回该对象,在所有其他情况下返回 this。
省略括号:
如果没有参数,我们可以省略 new 后的括号
let user = new User; // <-- 没有参数
// 等同于
let user = new User();
三.原型
构造函数的缺陷:
所以我们引入了构造函数原型prototype
1.构造函数原型prototype
构造函数通过原型分配的函数是所有对象所共享的
JavaScript规定每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
我们可以把不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function(){
// console.log('我会唱歌');
// }如果对象引用太多会开辟很多新的内存空间,会浪费资源
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('李德华', 18);
var zxy = new Star('张学友', 20);
ldh.sing();//我会唱歌
zxy.sing();//我会唱歌
一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
2.对象原型__proto__
对象都会有一个属性__proto__指向构造函数prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型存在
- __proto__对象原型和原型对象prototype是等价的
- __proto__对象原型的意义就是在于为对象的查找机制提供一个方法,或者说一条线路,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
3.constructor构造函数
对象原型(proto)和构造函数(prototype)原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身
constructor主要用于记录该对象引用哪个构造函数,它可以让原型对象重新指向原来的构造函数
构造函数、实例、原型对象三者之间的关系:
4.原型链
- 只要是对象就有__proto__原型,指向原型对象
- Star构造函数里面的原型对象(prototype)里面的__proto__原型指向的是object.prototype
所以这就构成了原型链:
正因为有了原型链,我们有了JavaScript的成员查找机制(规则):
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
- 如果还没有就查找原型对象的原型(object的原型对象)
- 依次类推一直找到object为止(null)
- __proto__对象原型的意义就在于为对象成员查找机制提供一个方法,或者说一条路线
5.原型对象中的this指向
原型对象函数里面的this指向的是实例对象
6.扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义方法。比如给数组增加自定义求偶数和的功能
注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式
原型对象的应用:
Array.prototype.sum = function(){
var sum = 0;
for (var i = 0; i < this.length; i++){
sum += this[i];
}
return sum;
}
var arr = [1,2,3];
console.log(arr.sum());//6
console.log(Array.prototype);//就可以看到sum方法被加到Array原型上
7.继承
ES6之前并没有给我们提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
- 我们首先需要了解call()
call函数最主要的作用:调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg,arg1,arg2....)
thisArg:当前调用函数this 的指向
arg1,arg2:传递的其他参数
- 借用构造函数继承父类型属性
核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性
原型题目:
四.执行上下文
js执行上下文:js代码在执行前,js引擎总要做一番工作,这份工作其实就是创建对应的执行上下文
执行上下文有且只有三类:全局执行上下文、函数上下文、与eval上下文
1.全局执行上下文
- 在执行全局代码前将window确定为全局执行上下文
- 对全局数据进行预处理:
var定义的全局变量:window为其赋值为undefined
function声明的全局函数:window为其赋值为fun
this:window为其赋值 - 开始执行全局代码
全局对象window上预定义了大量的方法和属性,我们在全局环境的任意处都能直接访问这些属性方法,同时window对象还是var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问。
2.函数执行上下文
函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。
说到这你是否会想,上下文种类不同,而且创建的数量还这么多,它们之间的关系是怎么样的,又是谁来管理这些上下文呢,这就不得不说说执行上下文栈了。
3.执行上下文栈
执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文。
- 在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
- 在全局执行上下文(window)确定后将其添加到栈中(压栈)
- 在函数执行上下文创建后,将其添加到栈中(压栈)
- 在当前函数执行完后,将栈顶的对象移除(出栈)
- 当所有的代码执行完后,栈中只剩下window
五.作用域
1.作用域
1.什么是作用域?
就是一块独立“地盘”,一个代码段所在的区域
它是静态的(相对于上下文对象),在编写代码时就确定了
2.作用域分为:全局作用域、函数作用域、没有块作用域(ES6有了)
3.作用域的作用:隔离变量,不同作用域下同名变量不会有冲突
定义几个函数+1(全局作用域) = 几个作用域
2.作用域和上下文的区别
- 区别1:
1.全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
2.全局执行上下文环境是在全局作用域确定之后,而作用域是js代码马上执行之前创建
3.函数执行上下文是在调用函数时,而作用域是函数体代码执行之前创建 - 区别2
1.作用域是静态的,只要函数定义好了就一直存在,且不会再变化
2.执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
两者之间的联系:
1.上下文环境(对象)是从属于所在的作用域
2.全局上下文环境->全局作用域
3.函数上下文环境->对应的函数使用域
3.作用域链
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),并且查找变量时就是沿着作用域链来查找的
向上一级一级的查找
六.闭包
高阶函数:是对其函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
在学习闭包之前,先看下变量作用域
变量作用域的不同分为两种:全局变量和局部变量
1.函数内部可以使用全局变量
2.函数外部不可以使用局部变量
3.当函数执行完毕,本作用域内的局部变量会销毁
什么是闭包:
闭包指有权访问另一个函数作用域中变量的函数
简单理解就是,一个作用域可以访问另外一个函数内部的局部变量
闭包的主要作用:延伸了变量的作用范围
闭包的生命周期:
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡:在嵌套的内部函数称为垃圾对象时
闭包的应用:定义js模块
1.具有特定功能的js文件
2.将所有的数据和功能都封装在一个函数内部(私有的)
七.正则表达式
什么是正则表达式:是用于匹配字符串中字符组合模式。在JavaScript中,正则表达式也是对象
可以用正则表达式完成表单验证
创建正则表达式: