一份认真总结的JS笔记-基础(不断更新新)

1,125 阅读10分钟

最近做项目时,对js一些基础东西还不是很熟练,经常要到网上搜,因此自己对JS一些基础知识做了一下总结。

1. 数据类型

数据类型\left\{
\begin{aligned}
String &  & 字符串 \\
Number &  & 数值 \\
Boolean &  & 布尔值 \\
Null &  & 空值 \\
Undefined &  & 未定义 \\
Object &  & 对象 \\
\end{aligned}
\right.

前五种是基本类型,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有三种方式:

  1. 调用 本身的toString()方法
  2. 使用String()函数
  3. 尝使用的方式 原类型+""
result1 = 1 + 2 + "3";
result2 = "1" + 2 + 3;

2.2 其他类型转换为Number 类型

其他类型转换为Number有两种方式:

  1. 使用Number()函数
  2. 针对字符串类型,使用parseInt() 和 parasefloat()
var height = "123px";
var heightNum = parseInt(height);

2.3 其他类型转换为Boolean 类型

其他类型转换为Boolean有两种方式:

  1. 使用Boolean()函数
  2. 隐式转换,借助非运算 !,对任意类型进行两次非运算,即可转化为布尔值
var str = "hyb";
var result = !!str;

console.log(result);
console.log(typeof result);

注意:第一个非运算其实就是调用Boolean()函数

3.对象

基本类型都是单一的值,值和值之间没有任何的联系

如果我们使用基本类型表示一个人的信息

var name = "喜欢前端的boy";
var gender = "男";
var age = 18;

如果使用基本类型的话,所建立的对象都是独立的,并不能成为一个整体。这时就需要使用对象这种数据类型。

对象属于混合的数据类型,在对象中可以保存多个不同的数据类型的属性

对象分类:

  1. 内键对象
    在ES标准中定义的对象,在任何的ES中都可以使用
    比如:Math,String,Nunber,Boolean,Function,Object...(可参考)
  2. 宿主对象
    由JS运行环境提供的对象,目前最主要的是由浏览器提供的对象
    比如:BOM,DOM(BOMDOM)
  3. 自定义对象
    开发人员根据业务需要定义的对象

3.1 对象创建

  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);
  1. 使用字面量创建对象
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);
	}

}

下面介绍一种创建对象的方式,这种方式在实际应用不怎么用

  1. 使用工厂方法创建对象
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这个类型,导致我们无法分辨不同类型的对象,下面我们介绍构造函数方式创建对象。

  1. 使用构造函数创建对象
<!-- 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. 函数

函数是一个对象
作用:封装一些功能(代码),等到需要时再执行这些功能(代码)

  1. 使用 构造函数 创建函数
var fun = new Function("console.log('hello world!')");
fun();

从上可以看出,使用构造函数创建函数对象时,键封装的代码以字符串的方式传递给构造函数
此种方法比较麻烦,不适用

  1. 使用 函数声明 创建函数
function sum(num1,num2){
	return num1 + num2;
}

sum(1,2);
  1. 使用 函数表达式 创建函数对象
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中有两种作用域

  1. 全局作用域
    直接在script标签内的JS代码,都是全局作用域
    在页面打开时创建,页面关闭时销毁
    在全局作用域中,创建的变量都会变为window对象的属性;创建的函数都会变为window的方法保存
var b = 123;

function fun(){
	console.log("fun");
}

console.log(window.b);
window.fun();
  1. 函数作用域
    调用函数时创建函数作用域,函数执行完毕,函数作用域销毁
    每次调用函数都会创建一个新的函数作用域,他们之间是项目独立的
    在函数作用域内可以访问全局作用域的变量
    在全据作用域访问不到函数作用域的变量
    当在函数作用域内操作一个变量时,他会在自身作用域内寻找,若有则直接使用,若无,则向全局作用域寻找
    在函数作用域内使用全局作用域的变量,在变量前面加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日