1.对象
使用基本数据类型,所创建的变量独立,不能成为一个整体。
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性。
对象的分类:
(1)内建对象:如Math Boolean String Object…
(2)宿主对象:由浏览器提供的对象 如BOM DOM console.log() document.write()
(3)自定义对象:由开发人员创建的对象
1.1 对象的创建
创建的三种形式:
第一种:
let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say = function () {
console.log("hello world");
}
第二种:
let obj = {}; // let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say = function () {
console.log("hello world");
}
第三种:
let obj = {
name: "lnj",
age: 33,
say: function () {
console.log("hello world");
}
};
1.2 使用new Object()创建对象,属性的增删改查
<script type="text/javascript">
//创建对象
/*
* 使用new关键字创建的函数,是构造函数constructor
* 构造函数是专门用来创建对象的函数
* 使用typeof 检查一个对象时,会返回object
* 在对象中保存的值称为属性
* 向对象中添加属性: 对象.属性名 = 属性值
* 读取属性: 对象.属性名 or 对象['属性名']
* 如果读取对象没有的属性,返回undefined
* 修改属性值: 对象.属性名 = 新的属性值
* 删除对象的属性 delete 对象.属性名
* 判断对象中是否有某属性: '属性名' in 对象 存在返回true 否则返回false
*/
var obj = new Object();
console.log(obj);
obj.name = "孙悟空";
obj.age = 500;
obj.gender = "男"; //向对象中添加三个属性
console.log(obj.name); //输出是"孙悟空"
console.log(obj.shifu); //输出是 undefined
obj.age = 50000; //修改属性值
console.log(obj.age); //输出是50000
delete obj.age; //删除对象的属性值
console.log(obj.age); //输出是undefined
</script>
#属性值的类型任意,可以是对象和函数
<script type="text/javascript">
var obj = new Object();
obj.name = "孙悟空";
/*
* JS对象的属性值可以是任意类型,甚至也可以是一个对象
*/
var obj2 = new Object();
obj2.name = "孙悟空";
obj.test = obj2;
document.write(obj.test); //输出是 [object Object]
console.log(obj.test); //输出是 Object {name:"孙悟空"}
console.log(obj.test.name); //输出是孙悟空
/*
* in 运算符: 检查一个对象中是否有指定的属性
* 如果有 返回true 否则返回false
* 语法: “属性名” in 对象
*/
console.log("name" in obj2); //true
console.log("name" in obj); //true
</script>
1.3 对象的方法
/*
*对象的属性值数据类型任意,甚至可以是一个函数
* 如果一个函数作为一个对象的属性保存,那么该函数称为这个对象的方法
* 调用该函数就是调用该对象的方法
*/
var obj = new Object();
obj.name = "孙悟空";
obj.age = "500";
obj.sayHello = function()
{
console.log(obj.name);
}
console.log(obj.sayHello);
/*
* 输出结果是
* function(){console.log(obj.name);}
*/
obj.sayHello(); //相当于调用匿名函数,输出是 孙悟空
1.4 属性的枚举
<script type="text/javascript">
/*
* 使用for ... in 语句,枚举对象中的属性
* 语法:for(var 变量 in 对象)
* {
//对象中有几个属性,循环体就执行几次
//每次执行时,会将对象中的一个属性名赋值给变量
//获取对象的属性值 对象[变量]
}
*/
var obj = {
name: "孙悟空",
age: 500,
address:"花果山"
}
for(var i in obj)
{
console.log(i);
/*
*输出是name,age,address
*/
}
for(var j in obj)
{
console.log(obj[j]);//输出是孙悟空,500,花果山
}
</script>
1.5 基本数据类型和引用数据类型的比较
<script type="text/javascript">
/*
* 基本数据类型在栈内存中保存的是变量值,各自独立不受影响。
* 对象在栈内存中保存的是地址,并在堆内存中开辟一个新的空间,保存该地址和属性值等。
* 当多个对象指向同一地址时,修改某个对象的属性,其他对象的该属性值会受影响。
*/
var obj = new Object();
obj.name = "孙悟空";
var obj2 = new Object();
obj2 = obj;
obj2 = null;
console.log(obj2); //null
console.log(obj); //Object{name:"孙悟空"}
</script>
<script type="text/javascript">
/*
* 当比较两个基本数据类型的值时,单纯的比较值
* 比较两个引用数据类型时,比较的是两个对象的内存地址。
*/
var obj = new Object();
obj.name = "孙悟空";
var obj2 = new Object();
obj2.name = "孙悟空";
console.log(obj == obj2); //false
var a = 1,b = 1;
console.log(a == b ); //true
</script>
基本类型:Number,String,Boolean,Null,Undefined
这些类型的值存放在栈区,函数调用时传递的是变量的值(值)。
引用类型:Object,Array,Function
这些类型的对象存放在堆区,对象的地址存放在栈区,函数调用时传递的是对象的地址(址)。
1 var a = [1,2,3,4,5];
2 var b = a;//传址 ,对象中传给变量的数据是引用类型的,会存储在堆中;
3 var c = a[0];//传值,把对象中的属性/数组中的数组项赋值给变量,这时变量C是基本数据类型,存储在栈内存中;改变栈中的数据不会影响堆中的数据
4 alert(b);//1,2,3,4,5
5 alert(c);//1
6 //改变数值
7 b[4] = 6; //此时b存储的是a的地址,改变b即改变b存储地址对应的对象也就是a地址对应的对象
8 c = 7;
9 alert(a[4]);//6 //所以a数组被改变了
10 alert(a[0]);//1
2.使用工厂模式创建对象
缺点:使用工厂模式创建的对象,使用的构造函数都是Object,所以创建的对象都是Object这个类型,就导致我们无法区分出多种不同类型的对象。
<script type="text/javascript">
/*
* 使用工厂模式,可以大批量创建对象
*/
function createPerson(name,age,address)
{
var obj = new Object();
obj.name = name;
obj.age = age;
obj.address = address;
return obj;
}
var obj1 = new createPerson("孙悟空",200,"花果山");
var obj2 = new createPerson("猪八戒",250,"高老庄");
var obj3 = new createPerson("白骨精",20,"盘丝洞");
console.log(obj1);//输出是 Object {name: "孙悟空", age: 200, address: "花果山"}
console.log(obj2);//输出是 Object {name: "猪八戒", age: 250, address: "高老庄"}
console.log(obj3);//输出是 Object {name: "白骨精", age: 20, address: "盘丝洞"}
</script>
3.使用构造函数创建对象
<script type="text/javascript">
/*
* 创建一个构造函数,来创建Person对象
* 构造函数也是函数,但是习惯首字母大写
* 构造函数和普通函数的区别:调用方式不同
* 普通函数是直接调用,构造函数是使用new关键字
*/
function Person(name,age,gender){
// let obj = new Object(); // 系统自动添加的
// let this = obj; // 系统自动添加的
this.name = name;
this.age = age;
this.gender = gender;
// return this; // 系统自动添加的
}
//构造函数调用
/*
* 构造函数的执行流程:
* 1.立即创建一个新的对象
* 2.将新建的对象设置为函数的this,在构造函数中可以使用this来引用新建的对象
* 3.逐行执行构造函数的代码
* 4.将新建的对象作为返回值返回
*
* 使用同一个构造函数创建的对象,是一类对象,也将一个构造函数称为一个类
* 将通过构造函数创建的对象,称为该类的实例
*/
var per2 = new Person("孙悟空",500,"monkey");
console.log(per2);//输出是 Person {name: "孙悟空", age: 500, gender: "monkey"}
/*
* 使用instanceof检查一个对象是否是一个类的实例
* 语法: 对象 instanceof 构造函数
* 如果是,返回true ,否则返回false
*/
function Dog(){}
var dog = new Dog();
console.log(dog);// 输出是 Dog {}
console.log(per2 instanceof Person);// true
console.log(dog instanceof Person); // false
console.log(per2 instanceof Object);// true
console.log(dog instanceof Object); // true
/*
* 所有对象都是object的后代,所以所有对象做Object的instanceof检查时都会返回true
*/
</script>
3.1 工厂模式和构造函数的比较
* 工厂模式:
* 函数名是小写
* 有new,
* 有返回值
* new之后的对象是当前的对象
* 直接调用函数就可以创建对象
*
* 自定义构造函数:
* 函数名是大写(首字母)
* 没有new
* 没有返回值
* this是当前的对象
* 通过new的方式来创建对象
3.2 判断对象的数据类型
3.2.1 实例对象.construct.name获取该实例对象所属的构造函数名
let obj = new Object();
console.log(typeof obj); // object
let arr = new Array();
// console.log(typeof arr); // object
console.log(arr.constructor.name); // Array
function Person() {
this.name = "lnj";
this.age = 34;
this.say = function () {
console.log(this.name, this.age);
}
}
let p = new Person();
// console.log(typeof p); // object
console.log(p.constructor.name); // Person
3.2.2 实例对象.constructor指向创建该对象的构造函数
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name)
}
}
var p2 = new Person('aaa', 20, 'doc') //创建一个Person对象(实例化对象),在这同时对属性进行初始化
console.dir(p2) //dir能显示结构
console.dir(Person)
console.log(p2.__proto__.constructor == Person.prototype.constructor) //true
//由构造函数创建的对象有constructor属性,指向构造函数
console.log(p2.constructor == Person) //true 判断对象的构造函数
/*
* 判断某个对象属于哪种数据类型:
* 1) 通过构造器的方式 实例对象.构造器==构造函数名字 p2.constructor == Person
* 2) 对象 instanceof 构造函数名字 p2 instanceOf Person
*/
3.2.3 instance0f判断实例对象
<script>
/*
instanceof用于判断 "对象" 是否是指定构造函数的 "实例"
只要 构造函数的原型对象出现在实例对象的原型链中都会返回true
*/
/*
class Person{
name = "lnj";
}
let p = new Person();
console.log(p instanceof Person); // true
class Cat{
name = "mm";
}
let c = new Cat();
console.log(c instanceof Person); // false
*/
function Person(myName) {
this.name = myName;
}
function Student(myName, myScore) {
Person.call(this, myName);
this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
let stu = new Student();
console.log(stu instanceof Person); // true
</script>
3.2.4 isPrototypeOf判断原型
<script>
/*
isPrototypeOf用于判断 一个对象是否是另一个对象的原型
2.1只要调用者在传入对象的原型链上都会返回true
*/
/*
class Person{
name = "lnj";
}
let p = new Person();
console.log(Person.prototype.isPrototypeOf(p)); // true
class Cat{
name = "mm";
}
console.log(Cat.prototype.isPrototypeOf(p)); // false
*/
function Person(myName) {
this.name = myName;
}
function Student(myName, myScore) {
Person.call(this, myName);
this.score = myScore;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
let stu = new Student();
console.log(Person.prototype.isPrototypeOf(stu)); // true
</script>
3.2.5 in和hasOwnProperty判断属性
<script>
// 需求: 判断某一个对象是否拥有某一个属性
class Person{
name = null;
age = 0;
}
Person.prototype.height = 0;
/*
let p = new Person();
// in的特点: 只要类中或者原型对象中有, 就会返回true
console.log("name" in p); // true
console.log("width" in p); // false
console.log("height" in p); // true
*/
// 需求: 判断某一个对象自身是否拥有某一个属性
let p = new Person();
// 特点: 只会去类中查找有没有, 不会去原型对象中查找
console.log(p.hasOwnProperty("name")); // true
console.log(p.hasOwnProperty("height")); // false
</script>
4.构造函数的缺陷
构造函数的局限:在构造函数内部创建方法,会使得每次创建实例都会创建一次该方法,但是将方法创建在全局作用域中,也存在风险。
<script type="text/javascript">
/*
* 创建一个Person构造函数
* 在Person构造函数中,为每一个对象都添加了一个sayHello方法
* 由于sayHello方法是在构造函数内部创建,所以构造函数每执行一次,
* 就会创建一个新的sayHello方法
* 也就是说,每个实例的sayHello方法都是唯一的
* 这样就导致构造函数每执行一次,就会创建一个新的方法
* 执行10000次就会创建10000个新的,一样的方法,浪费
* 改进方法:将方法在全局作用域中定义
*/
function Person(name,age,gender)
{
this.name = name ;
this.age= age;
this.gender = gender;
//创建sayHello方法
this.sayHello = function(){
console.log(this.name);
}
this.sayName = fun;
}
//在全局作用域中定义新方法fun,使Person的实例共享该方法
/*
* 将函数定义在全局作用域中很不安全
*/
function fun()
{
console.log(this.name);
}
var per1 = new Person("孙悟空",500,"f");
var per2 = new Person("猪八戒",501,"f");
console.log(per1.sayHello == per2.sayHello);// 输出是 false
console.log(per1.sayName == per2.sayName);// 输出是 true
</script>
5. 原型prototype
/*
1.prototype特点
1.1存储在prototype中的方法可以被对应构造函数创建出来的所有对象共享
1.2prototype中除了可以存储方法以外, 还可以存储属性
1.3prototype如果出现了和构造函数中同名的属性或者方法, 对象在访问的时候, 访问到的是构造函数中的数据
2.prototype应用场景
prototype中一般情况下用于存储所有对象都相同的一些属性以及方法
如果是对象特有的属性或者方法, 我们会存储到构造函数中
*/
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
this.currentType = "构造函数中的type";
this.say = function () {
console.log("构造函数中的say");
}
}
Person.prototype = {
currentType: "人",
say: function () {
console.log("hello world");
}
}
let obj1 = new Person("lnj", 34);
obj1.say(); //构造函数中的say
console.log(obj1.currentType); //构造函数中的type
<script type="text/javascript">
/*
* 原型prototype
* 每创建一个函数,解析器都会向函数内添加一个属性prototype,该属性对应一个对象——原型对象
* 如果函数作为普通函数调用,则prototype没有任何作用
* 当函数以构造函数的形式调用时,它所创建的对象中会有一个隐含的属性,
指向该构造函数的原型对象,通过__proto__来访问该属性
* 原型对象相当于一个公共区域,所有同一个类的实例都可以访问这个对象
* 可以将实例中共有的内容,设置到原型对象中
*/
function fun1()
{ }
var f1 = new fun1();
var f2 = new fun1();
console.log(f1.__proto__ == fun1.prototype); //true
console.log(f2.__proto__ == fun1.prototype); //true
console.log(f1.__proto__ == f2.__proto__); //true
/*
* 当访问构造函数的某个对象的属性或者方式时,会先在该对象中寻找
* 如果存在,直接使用
* 如果不存在,则在该构造函数的原型对象中寻找
* 如果存在,直接使用
*/
//向构造函数的原型对象中添加属性a
fun1.prototype.a = 123;
console.log(f1.a); //此时对象f1中不存在属性a,在构造函数fun1中寻找,输出是 123
//向对象f1中添加属性a
f1.a = "我是f1的属性a";
console.log(f1.a); //此时对象f1中存在属性a,直接调用,输出是 我是f1的属性a
//向构造函数的原型对象中添加方法sayHello
fun1.prototype.sayHello = function()
{
console.log("hello");
}
f1.sayHello(); //输出是 hello
检查对象中是否含有某个属性 , 原型链
/*
* 原型prototype
* 使用 in 检查某个对象中是否含有某个属性时,如果对象中没有
* 但是构造函数的原型对象中有,也会返回true
* 如果只想检查对象自己是否含有某个属性,而且不管原型对象中有没有,就不能使用 in
* 使用 对象.hasOwnProperty("属性名")
* 只有当对象自身含有该属性时,才会返回true
*/
function MyClass()
{}
MyClass.prototype.name = "原型对象的name属性";
var mc = new MyClass();
console.log("name" in mc); // true
console.log(mc.hasOwnProperty("name")); //false
/*
* 获取对象的属性或方法,先在对象自身寻找,如果没有,在原型对象中寻找
* 如果原型中也没有,去原型的原型中寻找,直到找到Object对象的原型
* Object对象没有原型(null)
* 原型对象也是对象,它也有原型
*/
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true
</script>
<script type="text/javascript">
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype.sayName = function() {
console.log('hello')
}
var p1 = new Person('aaa', 20, 'doc')
var p2 = new Person('bbb', 22, 'teac')
console.log(p1.sayName == p2.sayName) //true
p1.__proto__.sayName() //hello
Person.prototype.sayName() //hello
console.log(Person.prototype.constructor == Person) //true 构造函数.prototype.constructor 又指向了该构造函数本身
console.log(p1.__proto__.constructor == Person.prototype.constructor)
console.dir(Person.prototype.constructor)
/*
* 原型:对象.__proto__ 或者 构造函数.prototype 都是对象
* 原型的作用:共享数据,节省内存空间
* 实例对象中有个属性,__proto__,也是对象,叫原型,不是标准的属性,浏览器使用的
* 构造函数中有一个属性,prototype,也是对象,叫原型,是标准属性,程序员使用
* 构造函数.prototype.constructor 又指向了该构造函数本身
* 需要共享的数据写在原型中,不需要共享的数据写在构造函数中
* 如:Person.prototype.constructor 指向Person构造函数
* 实例对象中本来不存在构造函数中创建的方法,但是实例对象的__proto__属性指向了构造函数的原型对象,而构造函数的原型对象中存在方法,所以实例对象才能调用该方法
*/
</script>
原型对象的{}写法:需要指定原型的constructor指向
<script type="text/javascript">
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
//将要共享的属性和方法放在构造函数.prototype对象中.
//需要指定该对象的constructor指向该构造函数
Person.prototype = {
constructor:Person,
sex:'f',
study:function(){
console.log('今天好好学习了吗')
},
diet:function(){
console.log('今天控制饮食了吗')
}
}
console.dir(Person)
var p = new Person('jiao',23,'stu')
p.diet() //今天控制饮食了吗
</script>
原型中的方法可以互相访问
<script type="text/javascript">
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype.study = function(){
console.log('今天好好学习了吗')
this.eat()
}
Person.prototype.eat = function(){
console.log('今天好好吃饭了吗')
}
var p = new Person('jiao',23,'stu')
p.study() //今天好好学习了吗今天好好吃饭了吗
</script>
6.构造函数、原型对象和实例对象的关系
实例对象.proto == 构造函数.prototype
构造函数.prototype.constructor == 构造函数
实例对象的属性:__proto__
构造函数的属性:.prototype
构造函数可以创建实例对象
构造函数中有一个属性叫prototype,是构造函数的原型对象
构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数
实例对象的原型对象(__proto__)指向的是该构造函数的原型对象
构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的
7.Function函数
<script>
/*
1. 函数都是对象,都是通过Function构造函数创建出来的(函数都是Function构造函数的实例对象,都有__proto__属性)
2. Function构造函数的prototype属性指向Function的原型对象
3. Function的原型对象的constructor属性指向Functon构造函数
即Function === Function.prototype.constructor
*/
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
}
let obj1 = new Person("lnj", 34);
// Person构造函数是Function构造函数的实例对象, 所以也有__proto__属性
// Person构造函数的__proto__属性指向"Function原型对象"
console.log(Person.__proto__ === Function.prototype); // true
</script>
8.Object函数
<script>
/*
1.函数都是对象,所以Funciton构造函数也是对象
2.函数都是Function构造函数的实例对象
3.对象都有__proto__属性
4.Function构造函数有__proto__属性
// console.log(Function.__proto__ === Function.prototype); // true
5.Object也是构造函数,也有__proto__属性。Object构造函数的__proto__属性指向创建他的构造函数的原型对象
// console.log(Object.__proto__ === Function.prototype); // true
6.构造函数.prototype.constructor == 构造函数
// console.log(Object.prototype.constructor === Object); // true
7.Object构造函数的原型对象的__proto__属性指向null
// console.log(Object.prototype.__proto__); // null
*/
</script>
参考文章:Function和Object的关系
9.函数对象关系
<script>
/*
1.所有的构造函数都有一个prototype属性 指向自己的原型对象
2,所有的原型对象都有一个constructor属性 指向自己的构造函数
3.所有函数都是Function构造函数的实例对象
4.所有函数都是对象, 包括Function构造函数
5.所有对象都有__proto__属性
6.普通对象的__proto__属性指向创建它的那个构造函数对应的"原型对象"
7.所有对象的__proto__属性最终都会指向"Object原型对象"
8."Object原型对象"的__proto__属性指向NULL
*/
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
}
let obj1 = new Person("lnj", 34);
console.log(Function.prototype.__proto__);
console.log(Person.prototype.__proto__);
console.log(Function.prototype.__proto__ === Person.prototype.__proto__);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Person.prototype.__proto__ === Object.prototype);
</script>
10.原型链
原型链是实现继承的重要形式:
1.对象中__proto__组成的链条我们称之为原型链
2.对象在查找属性和方法的时候, 会先在当前对象查找
如果当前对象中找不到想要的, 会依次去上一级原型对象中查找
如果找到Object原型对象都没有找到, 就会报错。