JS的数据类型分类:基本数据类型
和引用数据类型
。
基本数据类型:
String
(字符串)、Number
(数值),Boolean
(布尔类型),Null
(空值)、Undefined
(未定义)引用数据类型:
Object
(对象):对象是一种复合数据类型,在对象中可以保存多个不同数据类型的属性基本数据类型保存在栈中,特点是值与值之间独立存在,修改一个变量的值不会影响另外一个
var a=123;
var b=a; // 此处是把a的值123直接赋给b,后面改变a的值不会影响b的值
a++;
console.log(a); // 124
cinsole.log(b); // 123
引用数据类型(对象)保存在堆中,特点是每次创建一个对象
都会在堆内存中开辟一段空间,变量保存的是对象的地址
(对象的引用),如果两个变量保存的是同一个对象的引用,通过一个变量修改属性时,另一个也会受影响。
对比基本数据类型:两个变量中存放的是同一个地址,我们修改的时候不是修改这变量的值(地址),而是修改该地址指向的内存中的值,也就是无论怎么修改,两个变量中的保存的地址值都没变,类似于同一个变量。
var obj=new Object(); // 此处变量中保存的值是,对象的地址。
obj.name="AISMALL";
var obj2=obj; // 本质就是两个变量指向一个地址,修改的时候是修改该地址指向的内存中的值。
obj.name="SMALL";
console.log(obj.name); // SMALL
cinsole.log(obj2.name); // SMALL
注意:比较两个变量时,对于基本数据类型,比较的就是值,对于引用数据类型比较的是地址,地址相同才相同。
内建对象
:由ES标准中定义的对象,在任何ES的实现中都可以使用,如:Math 、String、Number、Boolean、Function,Object等。宿主对象
:由JS的运行环境提供的对象,目前来讲主要指浏览器提供的对象,如,BOM、DOM。自定义对象
:由开发人员创建的对象方式一:使用new关键字
创建对象
var obj=new Object();
方式二:使用对象字面量
创建对象
var obj={ };
直接指定对象中的属性
。var obj={
name:"AISMALL",
age:29,
gender:"男"
}
var obj={
"name01":"AISMALL01",
"name02":"AISMALL02",
age:29,
gender:"男"
}
方式三:使用工厂方式创建对象
// fun为创建对象的工厂
function fun(name,age){
// 创建的对象
var obj=new Object();
// 向对象中添加属性
obj.name=name;
obj.age=age;
obj.sayName=function(){
console.log("name="+name+" "+"age="+age);
}
//将该对象返回
return obj;
}
// 使用工厂创建对象
var obj01=fun("AISMALL",18);
obj01.sayName();
由于JS中对于属性的命名没有任何要求
,不需要遵守标识符的规范,但是在开发中,尽量按照标识符的要求去写。
语法:
对象.属性名 = 属性值;
对象["属性名"] = 属性值;
如果使用的变量名特殊,可以使用这种方式给对象添加属性。注意:属性值可以任意数据类型,可以使基本数据类型,也可以是引用数据类型(对象里面套对象)
。
语法:
对象.属性名;
对象["属性名"];
注意:如果读取的属性对象中没有,并不会报错,而是返回undefined
。
如何检查一个对象中是否含有某个属性?in运算符
。
"属性名" in 对象
;
语法:
delete 对象.属性名;
delete 对象["属性名"];
当一个对象的属性是一个函数时,我们称这个函数是该对象的方法。
调用方式:对象.方法名();
定义在对象中的函数(方法)和生成对象的函数(构造函数),还有有点区别的:
函数中可以封装一些逻辑代码,在需要的时候可以去调用函数来执行这些代码。
创建函数有两种方式:
function 函数名([形参1,形参2...形参N]){
逻辑代码...
}
var 函数名 = function([形参1,形参2...形参N]){
语句...
};
语法:函数名([实参1,实参2…实参N]);
当我们调用函数时,函数中封装的代码会按照编写的顺序执行。
返回值:就是函数执行的结果,使用return 来设置函数的返回值。
语法:return 值;
可以通过一个变量来接收返回值,如果返回的值是一个字符串,举例如下:
// 定义函数
function fun1(){
return "Hello Javascript";
}
// 调用函数并用变量接收它的返回值
var str=fun1();
注意:
默认返回undefined
。调用函数时,可以在括号中传递实参,传递的实参会赋值给对应的形参,调用函数时JS解析器不会检查实参的类型
和个数
,可以传递任意数据类型
的值。
如果实参的数量大于形参,多余实参将不会赋值。
如果实参的数量小于形参,则没有对应实参的形参将会赋值undefined。
变量的作用域简单来说就是一个它们的作用范围。
在JS中作用域分成两种,全局作用域(全局变量)和函数作用域(局部变量)。
全局作用域:
script标签
中编写的代码都运行在全局作用域中,全局作用域在打开页面时创建,在页面关闭时销毁。window
,该对象由浏览器提供,可以在页面中直接使用,它代表的是整个的浏览器的窗口。尽量不要在全局中创建变量
,不好维护。函数作用域:
变量的声明提前:
var关键字
声明的变量会在所有的代码执行之前被声明
,但是不会赋值
。所以我们可以在变量声明前
使用变量,但是不使用var关键字声明的变量不会被声明提前。<script>
console.log("a="+a); //输出不会报错,输出为a=undefined
console.log("b="+b); //输出会报错
var a=10;
b=20;
</script>
前
被声明.函数的声明提前
函数声明
创建的函数,会在所有的代码执行之前被创建,也就是我们可以在函数声明前去调用函数,但是使用函数表达式
创建的函数没有该特性。<script>
fun1(); //不会报错
fun2(); //报错
function fun1(){
console.log("函数声明创建函数");
}
var fun2=function(){
console.log("函数表达式创建函数");
}
</script>
前
就被创建好了。类(class)这个概念来源于OOP(Object Oriented Programming),也就是面向对象编程,OOP是一种计算机编程架构,其有着封装,继承,多态三种特性。
信息封装
的基础。类的实例称为对象
。构造函数是专门用来创建对象的函数
,在JavaScript中一个构造函数我们也可以称为一个类
,通过一个构造函数创建的对象,我们称该对象是这个构造函数的实例,通过同一个构造函数
创建的对象,我们称为一类对象
。
在JS中可以通过构造函数创建对象,这个构造函数可以类比于Java中类中的构造函数。
function CrtObj(name,age,sex,addr){
// 在构造函数中,使用this,来指代对象
this.name = name;
this.age = age;
// 定义方法
this.funNameAge = function(){
console.log(this.name,this.age)
}
}
// 使用new关键字创建对象
const obj1 = new CrtObj('张三',18);
// 调用对象/实例化对象中的方法
obj1.funNameAge();
实际上并不存
在创建构造函数的特殊语法,其与普通函数唯一的区别在于调用方法
,对于任意函数,使用new关键字
调用,那么它就是构造函数,不使用new操作符调用,那么它就是普通函数。
按照惯例,我们约定构造函数名以大写字母开头,普通函数以小写字母开头,这样有利于显性区分二者,例如,new Array(),new Object()。
使用new操作符调用构造函数时,会经历4个阶段(new关键字干了什么)
// 在构造函数中,使用大驼峰命名法与普通函数区分
function CrtObj1(name,age){
// 定义属性
this.name = name;
this.age = age;
this.funNameAge1 = function(){
console.log(this.name,this.age)
}
}
// 通过构造函数来生成对象必须要和new关键词一起使用
const obj1 = new CrtObj1('张三',18);
// 调用对象/实例化对象中的方法
obj1.funNameAge1();
ES6中,新增了class类语法形式,它的作用和原理与ES5语法是完全相同的,只是语法形式不同而已。
class CrtObj2{
constructor(name,age){
this.name = name;
this.age = age;
}
funNameAge2(){
console.log(this.name , this.age);
}
}
const obj2 = new CrtObj2('李四',20);
obj2.funNameAge2();
ES6语法的特点:
引出:
function CrtObj(name,age){
this.name = name;
this.age = age;
// 定义方法
this.funNameAge = function(){
console.log(this.name,this.age)
}
}
const obj1 = new CrtObj('张三',18,'男','北京');
const obj2 = new CrtObj('张三',18,'男','北京');
obj1.funNameAge();
console.log( obj1.funNameAge == obj2.funNameAge ); //false
注意:
funNameAge
方法。funNameAge
方法,即所有实例的funNameAge
方法都是唯一的,不相同的。明明是同一个方法,却会分别占用内存,如何让所有对象共享同一个方法?
原型对象
来解决,具体操作如下:// 在构造函数中,使用大驼峰命名法与普通函数区分
function CrtObj(name,age){
this.name = name;
this.age = age;
// 定义方法
this.funNameAge = function(){
console.log(this.name,this.age)
}
}
//通过原型对象的方式添加一个方法
CrtObj.prototype.fun = function(){
console.log("通过原型对象的方式添加一个方法");
}
const obj1 = new CrtObj('张三',18,'男','北京');
const obj2 = new CrtObj('张三',18,'男','北京');
console.log( obj1.funfunNameAge == obj2.funNameAge ); //false
console.log( obj1.fun == obj2.fun ); // true ,这里的输出结果是true,内存地址是相同的了,这里可以类比Java中静态方法和普通方法
console.log( obj1.__proto__==CrtObj.prototype ); // true 实例化对象的原型属性指向创建它的构造函数的原型对象
解释一下原型属性(prototype)
和原型对象
:
prototype属性
,称为原型对象,是一个专门用来存储数据、函数的空间。_prto_
属性,称为原型属性,我们实例化对象的原型属性指向创建它的构造函数的原型对象。_proto_
找到这个构造函数prototype
属性中存储的方法。从而解决了上述问题。总结:
公共的区域
,凡是通过同一个构造函数创建的对象他们通常都可以访问到相同的原型对象,我们可以将对象中共有
的属性和方法统一添加到原型对象中,这样我们只需要添加一次,就可以使所有的对象
都可以使用。首先
会在该对象自身中寻找,如果有就直接使用,如果没有还会搜寻
该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。