JS笔记3.3--对象/工厂模式/构造函数/原型/Function和Object函数/原型链

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原型对象都没有找到, 就会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值