• javascript高级程序设计笔记-第六章(面向对象编程)


    一、对象属性

    1、属性类型

    数据属性的特性:

    • [[Configurable]]:能否通过delete删除属性,默认值为true
    • [[Enumerable]]:能否通过for-in循环返回属性,默认值为true
    • [[Writable]]:能否修改属性的值,默认值为true
    • [[Value]]:包含这个属性的值,默认是undefined

    直接在对象上定义的属性,[[Configurable]]、[[Enumerable]]、[[Writable]]都被设置为true,[[Value]]被设置为指定的值

    Object.defineProperty()方法,可以修改属性默认的特性

    //接收三个参数:属性所在对象、属性名字、描述符对象
    //描述符对象的属性必须是configurable、enumerable、writable、value
    var person = {};
    Object.defineProperty(person, "name", {
    	writable: false,
    	value: "Nicholas"
    });
    alert(person.name);			//Nicholas
    person.name = "Greg";
    alert(person.name);			//Nicholas(属性值被设置为不可修改)
    

    在调用Object.defineProperty()方法时,如果不指定,configurable、enumerable、writable的默认值都是false(与在对象上定义属性情况相反)

    2、访问器属性的特性:###

    • [[Configurable]]:能否通过delete删除属性,默认值为true
    • [[Enumerable]]:能否通过for-in循环返回属性,默认值为true
    • [[get]]:读取属性时调用的函数,默认值为undefined
    • [[set]]:写入属性时调用的函数,默认值为undefined

    设置一个属性的值导致其他属性发生变化

    var book = {
    	_year: 2004,			//下划线用于表示只能通过对象方法访问的属性
    	edition: 1
    };
    Object.defineProperty(book, "year", {
    	get: function() {
    		return this._year;
    	},
    	set: function(newValue) {
    		if (newValue > 2004) {
    			this._year = newValue;
    			this.edition += newValue - 2004;
    		}
    	}
    });
    book.year = 2005;
    alert(book.edition);		//2
    

    3、定义多个属性:###

    Object.defineProperties()方法,接收两个参数:要添加和修改其属性的对象、第二个对象的属性与第一个对象中要添加和修改的属性一一对应

    var book = {};
    Object.defineProperties(book, {
    	_year: {
    		writable: true,
    		value: 2004
    	},
    	edition: {
    		writable: true,
    		value: 1
    	},
    	year: {
    		get: function() {
    			return this._year;
    		},
    		set: function() {
    			if (newValue > 2004) {
    				this._year = newValue;
    				this.edition += newValue - 2004;
    			}
    		}
    	}
    });
    //在book对象上定义了两个数据属性和一个访问器属性
    

    4、读取属性的特性:###

    Object.getOwnPropertyDescriptor()读取给定属性的描述符,接收两个参数:属性所在对象、要读取其描述符的属性名称

    //承接上例
    var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
    alert(descriptor.value);			//2004
    alert(descriptor.configurable);		//false
    

    二、创建对象

    1、工厂模式

    function createPerson(name, age, job) {
    	var o = new Object();
    	o.name = name;
    	o.age = age;
    	o.job = job;
    	o.sayName = function() {
    		alert(this.name);
    	};
    	return o;
    }
    var person1 = createPerson("Nicholas", 29, "FE");
    var person2 = createPerson("Greg", 27, "Doctor");
    

    2、构造函数模式

    //构造函数始终都应该以一个大写字母开头
    function Person(name, age, job) {
    	this.name = name;
    	this.age = age;
    	this.job = job;
    	this.sayName = function() {
    		alert(this.name);
    	};
    }
    var person1 = new Person("Nicholas", 29, "FE");
    var person2 = new Person("Greg", 27, "Doctor");
    //不是同一个方法
    alert(person1.sayName === person2.sayName);	//false
    

    与工厂模式区别:

    • 没有显示创建对象
    • 直接将属性和方法赋予this对象
    • 没有return语句

    调用构造函数经历四步:

    1. 创建一个新对象
    2. 将构造函数的作用域赋给新对象(因此this指向这个新对象)
    3. 执行构造函数中的代码
    4. 返回新对象

    调用函数的几种方法:

    //当作构造函数使用
    var person = new Person("Nicholas", 29, "FE");
    person.sayName();		//Nicholas
    //作为普通函数使用
    Person("Greg", 27, "Doctor");
    window.sayName();		//Greg
    //在另一个对象的作用域中调用
    var o = new Object();
    Person.call(o, "John", 25, "Nurse");
    o.sayName();			//John
    

    3、原型模式

    让所有对象实例共享原型所保存的的属性和方法

    function Person() {}
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "FE";
    Person.prototype.sayName = function() {
    	alert(this.name);
    };
    var person1 = new Person();
    person1.sayName();		//Nicholas
    var person2 = new Person();
    person2.sayName();		//Nicholas
    
    alert(person1.sayName === person2.sayName);	//true
    

    简洁写法

    function Person() {}
    Person.prototype = {
    	name: "Nicholas",
    	age: 29,
    	job: "FE",
    	sayName: function() {
    		alert(this.name);
    	}
    };
    //区别是:constructor属性不再指向Person,而是指向Object构造函数(这种写法在重构原型函数)
    //下面设置constructor属性,但其现在默认是可枚举的
    function Person() {}
    Person.prototype = {
    	constructor: Person,
    	name: "Nicholas",
    	age: 29,
    	job: "FE",
    	sayName: function() {
    		alert(this.name);
    	}
    };
    //下面是把上例的constructor属性可枚举改为原生的不可枚举
    Object.defineProperty(Person.prototype, "constructor", {
    	enumerable: false,
    	value: Person
    });
    

    isPrototypeOf()方法,测试对象之间的关系

    alert(Person.prototype.isPrototypeOf(person1));		//true
    alert(Person.prototype.isPrototypeOf(person2));		//true
    

    Object.getPrototypeOf()方法,取得原型对象中的值

    alert(Object.getPrototypeOf(person1) === Person.prototype);	//true
    alert(Object.getPrototypeOf(person1).name);			//Nicholas
    

    读取某个对象的属性时,首先从对象实例本身开始,如果没有找到,再继续搜索指针指向的原型对象

    可以创建同名属性屏蔽原型中的属性,但是无法修改原型中的属性

    hasOwnProperty()方法,检测一个属性是存在于实例中,还是存在于原型中

    function Person() {}
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "FE";
    Person.prototype.sayName = function() {
    	alert(this.name);
    };
    var person1 = new Person();
    alert(person1.name);						//Nicholas
    alert(person1.hasOwnProperty("name"));		//false
    
    person1.name = "Greg";
    alert(person1.name);						//Greg
    alert(person1.hasOwnProperty("name"));		//true
    
    delete person1.name
    alert(person1.name);						//Nicholas
    alert(person1.hasOwnProperty("name"));		//false
    

    原型与in操作符:

    • 单独使用in操作符,只要对象能够访问到给定属性就能返回true,而不管属性是在原型还是实例中
    • 在for-in循环中,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括实例中的属性、也包括原型中的属性

    同时使用in操作符和hasOwnProperty()方法,可以确定该属性是存在于对象中,还是原型中

    function hasPrototypeProperty(object, name) {
    	return !object.hasOwnProperty(name) && (name in object);
    }
    

    原型中的基本属性和方法(具体见基本概念中Object类型那一节)都不可枚举(后来添加到原型中的属性和方法默认可枚举)、实例中的添加的属性和方法默认可枚举(不使用Object.defineProperty()方法)

    var o = {
    	name: "Nicholas",
    };
    for (var propName in o) {
    	alert(propName);		//name
    }
    //o的原型中的基本属性和方法都没有枚举
    
    function Person() {}
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "FE";
    Person.prototype.sayName = function() {
    	alert(this.name);
    };
    var keys = new Person();
    for (var propName in keys) {
    	alert(propName);		//name,age,job,sayName
    }
    //后来添加到原型中的属性和方法默认可以枚举
    

    Object.keys()方法,接收一个对象作为参数,返回该对象所有可枚举属性的字符串数组

    function Person() {}
    Person.prototype.name = "Nicholas";
    Person.prototype.age = 29;
    Person.prototype.job = "FE";
    Person.prototype.sayName = function() {
    	alert(this.name);
    };
    var keys = Object.keys(Person.prototype);
    console.log(keys);		//["name", "age", "job", "sayName"](原型的constructor属性默认不可枚举)
    
    var p1 = new Person();
    p1.name = "Bob";
    p1.age = 31;
    var p1keys = Object.keys(p1);
    console.log(p1keys);			//["name", "age"](只限该对象,不看Person.prototype)
    //上下两例比较
    var p1 = new Person();
    p1.name = "Bob";
    p1.age = 31;
    for (var propName in p1) {
    	console.log(propName);	//name,age,job,sayName(该对象和原型中所有可枚举的)
    }
    

    Object.getOwnPropertyNames()方法,获得所有实例属性,无论是否可枚举

    var keys = Object.getOwnPropertyNames(Person.prototype);
    console.log(keys);			//["constructor", "name", "age", "job", "sayName"]
    
    var p1 = new Person();
    p1.name = "Bob";
    p1.age = 31;
    console.log(Object.getOwnPropertyNames(p1));	//["name", "age"](只限该对象)
    

    原型的动态性:对原型对象所做的任何修改都能够立即从实例上反映出来,但是通过简洁写法重写原型对象时,如果之前已经创建了实例,情况就不一样了

    function Person() {}
    var friend = new Person();
    Person.prototype = {
    	constructor: Person,
    	name: "Nicholas",
    	age: 29,
    	job: "FE",
    	sayName: function() {
    		alert(this.name);
    	}
    };
    friend.sayName();			//报错
    

    如图,因为实例中的指针仅指向原型,而不指向构造函数,所以原型修改为另一个对象,就等于切断了构造函数与最初原型之间的联系

    原型对象的问题

    function Person() {}
    Person.prototype = {
    	constructor: Person,
    	name: "Nicholas",
    	age: 29,
    	job: "FE",
    	friends: ["Bob", "Amy"],
    	sayName: function() {
    		alert(this.name);
    	}
    };
    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push("Van");
    console.log(person1.friends);		//["Bob", "Amy", "Van"]
    console.log(person2.friends);		//["Bob", "Amy", "Van"]
    //Van添加到原型对象中
    

    4、组合使用构造函数和原型模式

    构造函数用于定义实例属性,原型模式定义方法和共享的属性。

    每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的引用

    function Person(name, age, job) {
    	this.name = name;
    	this.age = age;
    	this.job = job;
    	this.friends = ["Bob", "Amy"];
    }	
    Person.prototype = {
    	constructor: Person,
    	sayName: function() {
    		alert(this.name);
    	}
    }
    
    var person1 = new Person();
    var person2 = new Person();
    
    person1.friends.push("Van");
    console.log(person1.friends);		//["Bob", "Amy", "Van"]
    console.log(person2.friends);		//["Bob", "Amy"]
    console.log(person1.friends === person2.friends);	//false
    console.log(person1.sayName === person2.sayName);	//true
    

    三、继承

    1、原型链

    利用原型让一个引用类型继承另一个引用类型的属性和方法:
    让原型对象等于另一个对象的实例,此时原型对象将包含一个指向另一个原型的指针,相应的另一个原型也包含一个指向另一个构造函数的指针

    function SuperType() {
    	this.property = true;
    }
    SuperType.prototype.getSuperValue = function() {
    	return this.property;
    };
    function SubType() {
    	this.subproperty = false;
    }
    //继承SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function() {
    	return this.subproperty;
    }
    //创建实例
    var instance = new SubType();
    alert(instance.getSuperValue());		//true
    

    原型链的问题

    function SuperType() {
    	this.colors = ["red", "green", "blue"];
    }
    function SubType() {}
    SubType.prototype = new SuperType();
    var instance1 = new SubType();
    instance1.colors.push("black");
    console.log(instance1.colors);		//["red", "green", "blue", "black"]
    var instance2 = new SubType();
    console.log(instance2.colors);		//["red", "green", "blue", "black"]
    

    当SubType通过原型链继承了SuperType之后,SubType.prototype就变成了SuperType的一个实例,因此它也拥有了一个它自己的colors属性(类似于专门创建了一个SubType.prototype.colors属性一样)。结果SubType所有实例都会共享这个colors属性。对其中一个实例的colors进行修改,其它实例的colors属性也会同步修改

    注意不要使用对象字面量创建原型方法,因为这样会重写原型链

    2、借用构造函数

    在子类型构造函数的内部调用超类型构造函数

    function SuperType() {
    	this.colors = ["red", "blue", "green"];
    }
    function SubType() {
    	SuperType.call(this);			//借调超类型的构造函数
    }
    var instance1 = new SubType();
    instance1.colors.push("black");
    console.log(instance1.colors);		//["red", "green", "blue", "black"]
    var instance2 = new SubType();
    console.log(instance2.colors);		//["red", "blue", "green"]
    

    3、组合继承

    使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承

    function SuperType(name) {
    	this.name = name;
    	this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function() {
    	console.log(this.name);
    };
    function SubType(name, age) {
    	//借用构造函数,继承属性
    	SuperType.call(this, name);
    	this.age = age;
    }
    //继承方法
    SubType.prototype = new SuperType();
    SubType.prototype.constrcutor = SubType;
    SubType.prototype.sayAge = function() {
    	console.log(this.age);
    };
    //创建实例
    var instance1 = new SubType("Nicholas", 29);
    instance1.colors.push("black");
    console.log(instance1.colors);	//["red", "blue", "green", "black"]
    instance1.sayName();			//Nicholas
    instance1.sayAge();				//29
    var instance2 = new SubType("Greg", 28);
    console.log(instance2.colors);	//["red", "blue", "green"]
    instance2.sayName();			//Greg
    instance2.sayAge();				//28
    

    4、原型式继承

    借助原型基于已有的对象创建新对象

    function object(o) {
    	function F() {}
    	F.prototype = o;
    	return new F();
    }
    var person = {
    	name: "x",
    	friends: ["a", "b", "c"]
    };
    var person1 = object(person);
    person1.name = "y";
    person1.friends.push("d");
    
    var person2 = object(person);
    person2.name = "z";
    person2.friends.push("e");
    
    console.log(person.friends);		//["a", "b", "c", "d", "e"]
    

    5、寄生式继承

    创建一个仅用于封装继承过程的函数

    function object(o) {
    	function F() {}
    	F.prototype = o;
    	return new F();
    }
    function createAnother(original) {
    	var clone = object(original);
    	clone.sayHi = function() {
    		alert("hi");
    	};
    	return clone;
    }
    var person = {
    	name: "x",
    	friends: ["a", "b", "c"]
    };
    var person1 = createAnother(person);
    person1.sayHi();				//"hi"
    console.log(person1.friends);	//["a", "b", "c"]
    //新对象不仅有person所有的属性和方法,还有自己的sayHi()方法
    

    6、寄生组合式继承

    function inheritPrototype(subType, superType) {
    	var prototype = Object(superType.prototype);	//创建对象
    	prototype.constrcutor = subType;				//增强对象
    	subType.prototype = prototype;					//指定对象
    }
    function SuperType(name) {
    	this.name = name;
    	this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function() {
    	alert(this.name);
    };
    function SubType(name, age) {
    	SuperType.call(this, name);
    	this.age = age;
    }
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function() {
    	alert(this.age);
    }
  • 相关阅读:
    Markdown
    DNS解析流程
    maven 的各种命令
    ES6初体验——(1)let和const命令
    table相关的选择器 & children()与find()的区别 & 选择器eq(n)与nth-child(n)的差异
    Java MD5加密类
    POI操作Excel异常Cannot get a text value from a numeric cell
    MyEclipse+SSH开发环境配置
    JdbcTemplate详解
    Spring配置声明
  • 原文地址:https://www.cnblogs.com/u14e/p/5234424.html
Copyright © 2020-2023  润新知