最近做项目时,对js一些基础东西还不是很熟练,经常要到网上搜,因此自己对JS一些基础知识做了一下总结。
1. 数据类型
前五种是基本类型,Object属于引用类型
变量类型的检测:
typeof 运算符,用法:typeof(表达式) 或 typeof 变量名
// 字符串 输出:String
console.log(typeof 'hyb');
// 数值 输出:Number
console.log(typeof 88);
// 布尔 输出:boolean
console.log(typeof true);
let object = null;
// 空值 输出:object
console.log(typeof object);
let sum;
let undefinedVal = undefined;
// 声明不赋值 和 赋值为undefined 输出:undefined
console.log(typeof sum);
console.log(typeof undefinedVal);
const person = {
name:"晚安大前端",
age:18,
}
// 对象 输出:object
console.log(typeof person);
两点注意:
1. 空值(Null)检测的值是:object,通常用法是先定义一个空的对象,用到时赋值。
2. 当声明一个变量时,并不赋值,检查的值就是undefined
2. 类型转换
2.1 其他类型转换为String 类型
其他类型转换为String有三种方式:
- 调用 本身的toString()方法
- 使用String()函数
- 尝使用的方式 原类型+""
result1 = 1 + 2 + "3";
result2 = "1" + 2 + 3;
2.2 其他类型转换为Number 类型
其他类型转换为Number有两种方式:
- 使用Number()函数
- 针对字符串类型,使用parseInt() 和 parasefloat()
var height = "123px";
var heightNum = parseInt(height);
2.3 其他类型转换为Boolean 类型
其他类型转换为Boolean有两种方式:
- 使用Boolean()函数
- 隐式转换,借助非运算 !,对任意类型进行两次非运算,即可转化为布尔值
var str = "hyb";
var result = !!str;
console.log(result);
console.log(typeof result);
注意:第一个非运算其实就是调用Boolean()函数
3.对象
基本类型都是单一的值,值和值之间没有任何的联系
如果我们使用基本类型表示一个人的信息
var name = "喜欢前端的boy";
var gender = "男";
var age = 18;
如果使用基本类型的话,所建立的对象都是独立的,并不能成为一个整体。这时就需要使用对象这种数据类型。
对象属于混合的数据类型,在对象中可以保存多个不同的数据类型的属性
对象分类:
- 内键对象
在ES标准中定义的对象,在任何的ES中都可以使用
比如:Math,String,Nunber,Boolean,Function,Object...(可参考) - 宿主对象
由JS运行环境提供的对象,目前最主要的是由浏览器提供的对象
比如:BOM,DOM(BOM,DOM) - 自定义对象
开发人员根据业务需要定义的对象
3.1 对象创建
- 使用new关键字创建对象
var obj = new Object();
// 添加属性
obj.name = "hyb";
obj.gender = "男";
// 修改
obj.name = "喜欢前端的boy";
// 删除
delete obj.gender;
// 查询
console.log(obj.name);
<!--undefined-->
console.log(obj.gender);
- 使用字面量创建对象
var obj = {
name: "hyb",
age: 18
};
obj.name = "孙武";
obj.gender = "男";
console.log(obj);
如果使用字面量的方式创建多个类似对象,会有很多类似的代码,比如:
var obj1 = {
name: "张三",
age: 12,
gender: "男",
sayName: function(){
console.log(this.name);
}
}
var obj2 = {
name: "李四",
age: 18,
gender: "男",
sayName: function(){
console.log(this.name);
}
}
var obj2 = {
name: "王五",
age: 22,
gender: "男",
sayName: function(){
console.log(this.name);
}
}
下面介绍一种创建对象的方式,这种方式在实际应用不怎么用
- 使用工厂方法创建对象
function creatObject(name,age,gender){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function(){
console.log(obj.name);
}
return obj;
}
var obj1 = creatObject("张三",12,"男");
var obj2 = creatObject("李四",28,"男");
var obj3 = creatObject("王五",67,"男");
var dog = creatObject("泰迪",2,"男")
使用工厂方法创建的对象,使用的构造函数都是Object。因此,所创建出的对象都是Object这个类型,导致我们无法分辨不同类型的对象,下面我们介绍构造函数方式创建对象。
- 使用构造函数创建对象
<!-- Person构造函数 -->
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
console.log(this.name);
}
console.log(this);
}
// Animal构造函数
function Animal(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayHello = function(){
console.log("I'm" + this.name + ",汪汪汪汪!");
}
}
var per = new Person("前端阿甘",22,"男");
var dog = new Animal("泰迪",2,"male");
// Person
console.log(per);
// Animal
console.log(dog);
// 使用 instanceof 可以检查一个对象是否是一个类的实例
// true
console.log(per instanceof Person);
// false
console.log(per instanceof Animal);
console.log(per instanceof Object);
// 当普通函数 用
Person("前端阿甘",22,"男");
console.log(window.name);
console.log(name);
构造函数就是一个普通的函数,创建方式和普通函数没区别
不同的是构造函数习惯上首字母大写
构造函数和普通函数的区别主要在于调用方式
普通函数直接调用,构造函数需要使用new关键字来调用
调用构造函数时,内部执行的流程
- 立即创建一个新的对象
- 将新的对象设置为函数中的this,在构造函数中使用this来引用新建的的对象
- 执行函数中的代码
- 将新建的对象作为返回值返回
在Person构造方法中,每创建一个Person对象,便会创建一个新的sayName()方法,所有的sayName()都是唯一的。这样会完全是没必要的,完全可以使所有的Person对象都共享一个sayName()方法,第一种方式使用函数赋值来改造
<!-- Person构造函数 -->
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = foo;
}
function foo(){
console.log(this);
console.log(this.name);
}
var per1 = new Person("张三", 23, "男");
var per2 = new Person("王五", 35, "女");
<!--是否是同一个sayName-->
console.log(per1.sayName == per2.sayName);
per1.sayName();
foo();
将方法在全局作用域内定义,会产生两个问题:
- 污染了全局作用域的命名空间
- 不安全
5. 使用工厂方法创建对象
那么就可以用原型(prototype)来解决这个问题
我们创建的每一个函数,解析器都会向函数中传递一个属性prototype
这个属性对应着一个对象,这个对象我们称为原型对象
如果函数作为普通对象调用时,prototype 没有任何作用
当函数以构造函数的作用调用时,创建的对象中都有一个隐含的属性(proto)指向该构造函数的原型对象
原型对象相当于一个公共区域,所有同一个类的实例都可以访问到这个原型对象,因此我们可以将对象中的共有的内容,统一设置到原型对象中
当访问对象的一个属性和方法时,会先在对象自身中查找,若无,就会去原型对象中寻找
因此,我们可将上边代码修改为:
<!-- Person构造函数 -->
function Person(name){
this.name = name;
}
var foo = new Person("hyb");
console.log(foo.__proto__ == Person.prototype);
Person.prototype.age = 24;
Person.prototype.sayName = function(){
console.log(this.name);
}
console.log(foo.age);
foo.sayName();
<!--使用hasOwnProperty 判断自身属性是否存在-->
上面代码中,构造函数、实例和原型对象的关系,如下图所示

in 运算符
通过此运算符可以检查一个对象中是否有指定的属性
若有返回 true,没有返回false
var obj = new Object();
obj.name = "hyb";
console.log("name" in obj);
console.log("gender" in obj);
属性遍历
使用for...in 可以枚举对象中的所有属性
var obj = {
name: "喜欢前端的boy",
age: 18,
sayName: function(){
console.log(this.name);
}
}
console.log("sayName" in obj);
for (var property in obj){
console.log(property);
// 不好使
// console.log(obj.property);
console.log(obj[property]);
console.log(typeof obj[property]);
}
基本类型和引用类型的保存
js的变量都是保存到栈内存中的
基本类型的值是直接保存到栈内存中,值与值之间是独立的,修改一个变量不会对其他变量产生影响,如下图:

对象是保存到堆内存的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间
而变量保存的是对象的内存地址,见下图:

4. 函数
函数是一个对象
作用:封装一些功能(代码),等到需要时再执行这些功能(代码)
- 使用 构造函数 创建函数
var fun = new Function("console.log('hello world!')");
fun();
从上可以看出,使用构造函数创建函数对象时,键封装的代码以字符串的方式传递给构造函数
此种方法比较麻烦,不适用
- 使用 函数声明 创建函数
function sum(num1,num2){
return num1 + num2;
}
sum(1,2);
- 使用 函数表达式 创建函数对象
var sum = function (num1,num2){
return num1 + num2;
}
console.log(sum(1,2));
解析器在调用函数时,就会向函数内部传递一个隐含的参数
这个参数就是this,this指向的是一个对象
这个对象称为函数执行的上下文对象
function foo(){
console.log(this);
}
foo();
立即执行函数
直接执行匿名函数,就叫立即执行函数,在后边添加参数
(function (num1,num2){
console.log(num1 + num2)
})(1,2);
vds
5. 方法
方法 VS 函数
当一个函数作为一个对象的属性时,那么我们称这个函数为此对象的方法
第一种方式:
var obj = new Object();
obj.name = "hyb";
obj.sayName = function(){
console.log(obj.name);
}
obj.sayName();
第二种方式:
var obj = {
name: "喜欢前端的boy",
sayName: function(){
console.log(this.name);
}
}
obj.sayName();
6. 作用域
作用域指的是一个变量作用的范围
在js中有两种作用域
- 全局作用域
直接在script标签内的JS代码,都是全局作用域
在页面打开时创建,页面关闭时销毁
在全局作用域中,创建的变量都会变为window对象的属性;创建的函数都会变为window的方法保存
var b = 123;
function fun(){
console.log("fun");
}
console.log(window.b);
window.fun();
- 函数作用域
调用函数时创建函数作用域,函数执行完毕,函数作用域销毁
每次调用函数都会创建一个新的函数作用域,他们之间是项目独立的
在函数作用域内可以访问全局作用域的变量
在全据作用域访问不到函数作用域的变量
当在函数作用域内操作一个变量时,他会在自身作用域内寻找,若有则直接使用,若无,则向全局作用域寻找
在函数作用域内使用全局作用域的变量,在变量前面加window
var a = 10;
function foo(){
var a = "全局作用域";
console.log(a);
console.log(window.a);
var b = 20;
}
console.log(b);
foo();
7. 变量声明提前
如果使用var关键字声明的对象,会在所在 作用域 代码执行之前被声明,但是不会被赋值
但是如果声明变量时不使用var关键字,则变量不会被声明提前
var a = 10;
function foo(){
console.log(a);
var a = 20;
console.log(a);
}
foo();
上边的代码结果是:undefined,20
上边的代码等价于:
var a = 10;
function foo(){
var a;
console.log(a);
a = 20;
console.log(a);
}
foo();
那如果是下段代码那?
var a = 10;
function foo(){
console.log(a);
a = 20;
console.log(a);
}
foo();
8. 函数声明提前
使用 函数声明 创建的函数对象,会在调用之前创建
使用 函数表达式创建的对象则不会
foo();
function foo(){
console.log("声明会提前");
}
goo();
var goo = function (){
console.log("声明不会被提前");
}
9. 数组(Array)
数组是一个对象,不同的是普通对象使用字符串座位属性名,而数组是使用数字作为索引操作对象中的元素
数组的存储性能比普通对象要好
数组创建
<!-- 1. 构造函数 -->
var arr1 = new Array(10, 20);
// 2.字面量的方式
var arr2 = ["前端阿甘","莫颜"];
console.log(typeof arr1, typeof arr2);
创建数组的两种方式:构造函数的方式 和 字面量的方式
数组的几个方法
var arr = new Array("孙悟空", "猪八戒");
/**
1. push()
功能:向数组末尾添加一个或多个元素
返回:数组的长度
*/
var arrR1 = arr.push("唐僧","红孩儿");
console.log(arrR1);
/**
2. unshift()
功能:向数组末尾添加一个或多个元素
返回:数组的长度
*/
var arrR2 = arr.unshift("唐僧","红孩儿");
console.log(arrR2);
/**
3. pop()
功能:删除数组中的最后一个元素
返回:返回删除的元素
*/
var arrR3 = arr.pop();
console.log(arrR3);
/**
4. shift()
功能:删除数组中的最后一个元素
返回:返回删除的元素
*/
var arrR4 = arr.shift();
console.log(arrR4);
数组遍历
var arr = new Array("孙悟空", "猪八戒","唐僧","红孩儿");
/**
1.for 循环
功能:向数组末尾添加一个或多个元素
返回:数组的长度
*/
for(var i = 0; i < arr.length; i++) {
console.log(i,arr[i]);
}
/**
2.forEach 循环
兼容性:支持IE8以上的浏览器
参数:函数
浏览器在回调函数中传递三个参数
1. 当前遍历的数组元素
2. 当前遍历的数组元素索引
3. 数组
*/
arr.forEach((value, index, arr) => {
console.log(value, index, arr);
})
以上是两种方式遍历数组:
- for
- forEach
10. Date
Date对象用于处理日期和时间
版本说明
V1.0_数据类型 2019年11月26日
V1.1_类型转换 2019年12月11日
V1.2_对象 2019年12月13日