JavaScript面向对象
关于对象的基本概念就不多写,已经学了无数次
1.创建默认对象
-
先创建对象,在添加属性和行为,有两种方式
// 创建对象的第一种方式 let obj = new Object(); obj.name = "tan"; obj.age = 20; obj.say = function () { console.log("hello world"); } console.log(obj.name); console.log(obj.age); obj.say();
// 创建对象的第二种方式 let obj = {}; // let obj = new Object(); obj.name = "tan"; obj.age = 20; obj.say = function () { console.log("hello world"); } console.log(obj.name); console.log(obj.age); obj.say();
-
创建对象时添加属性和行为
// 创建对象的第三种方式 // 注意点: 属性名称和取值之间用冒号隔开, 属性和属性之间用逗号隔开 let obj = { name: "tan", age: 20, say: function () { console.log("hello world"); } }; console.log(obj.name); console.log(obj.age); obj.say();
2.方法和函数的区别
- 函数就是没有和其它的类显示的绑定在一起的, 我们就称之为函数
- 方法就是显示的和其它的类绑定在一起的, 我们就称之为方法
- 无论是函数还是方法, 内部都有一个叫做this的东西,this是什么? 谁调用了当前的函数或者方法, 那么当前的this就是谁,函数内部的this输出的是window, 方法内部的this输出的是当前调用的那个对象.
3.工厂函数(不专业)
工厂函数就是专门用于创建对象的函数, 我们就称之为工厂函数,可以减少代码冗余
function createPerson(myName, myAge) { let obj = new Object(); obj.name = myName; obj.age = myAge; obj.say = function () { console.log("hello world"); } return obj; } let obj1 = createPerson("tan", 20); let obj2 = createPerson("zs", 25); console.log(obj1); console.log(obj2);
4.构造函数(注意当new一个对象时内部做了什么)
-
4.1 构造函数和工厂函数一样, 都是专门用于创建对象的
-
构造函数和工厂函数的区别
构造函数的函数名称首字母必须大写
构造函数只能够通过new来调用
function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = function () { console.log("hello world"); } // return this; // 系统自动添加的 } let obj1 = new Person("tan", 20); let obj2 = new Person("zs", 44); console.log(obj1); console.log(obj2); /* 1.当我们new Person("tan", 20);系统做了什么事情 1.1会在构造函数中自动创建一个对象 1.2会自动将刚才创建的对象赋值给this 1.3会在构造函数的最后自动添加return this; */
4.2 构造函数里需要注意的一些性能问题
function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = function () { console.log("hello world"); } // return this; // 系统自动添加的 } let obj1 = new Person("lnj", 34); let obj2 = new Person("zs", 44); // 由于两个对象中的say方法的实现都是一样的, 但是保存到了不同的存储空间中 // 所以有性能问题 console.log(obj1.say === obj2.say); // false 地址不一样
-
怎样解决上边的问题呢,看一下优化的演变过程
构造函数优化1
function mySay() { console.log("hello world"); } function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = mySay; // return this; // 系统自动添加的 } /* 1.当前这种方式解决之后存在的弊端 1.1阅读性降低了 1.2污染了全局的命名空间 */
构造函数优化2
let fns = { mySay: function () { console.log("hello world"); } } function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; this.say = fns.mySay; // return this; // 系统自动添加的 } let obj1 = new Person("lnj", 34); let obj2 = new Person("zs", 44); console.log(obj1.say === obj2.say); // true
构造函数优化终极(专业写法)
function Person(myName, myAge) { // let obj = new Object(); // 系统自动添加的 // let this = obj; // 系统自动添加的 this.name = myName; this.age = myAge; // this.say = fns.mySay; // return this; // 系统自动添加的 } //每个"构造函数"中都有一个默认的属性, 叫做prototype, prototype属性保存着一个对象,可以在里面定义方法,然后创建的方法可以在创建对象时使用,如下 Person.prototype = { say: function () { console.log("hello world"); } } let obj1 = new Person("lnj", 34); obj1.say(); let obj2 = new Person("zs", 44); obj2.say(); console.log(obj1.say === obj2.say); // true
-
4.3 prototype特点
- 存储在prototype中的方法可以被对应构造函数创建出来的所有对象共享
- prototype中除了可以存储方法以外, 还可以存储属性
- prototype如果出现了和构造函数中同名的属性或者方法, 对象在访问的时候, 访问到的是构造函中的数据
4.4 prototype应用场景
- prototype中一般情况下用于存储所有对象都相同的一些属性以及方法
- 如果是对象特有的属性或者方法, 我们会存储到构造函数中
5.对象三角恋关系(好好好好的理解)
/* 1.每个"构造函数"中都有一个默认的属性, 叫做prototype prototype属性保存着一个对象, 这个对象我们称之为"原型对象" 2.每个"原型对象"中都有一个默认的属性, 叫做constructor constructor指向当前原型对象对应的那个"构造函数" 3.通过构造函数创建出来的对象我们称之为"实例对象" 每个"实例对象"中都有一个默认的属性, 叫做__proto__ __proto__指向创建它的那个构造函数的"原型对象" */ function Person(myName, myAge) { this.name = myName; this.age = myAge; } let obj1 = new Person("lnj", 34); console.log(Person.prototype); console.log(Person.prototype.constructor); console.log(obj1.__proto__);
5.1Function函数
/* 1.JavaScript中函数是引用类型(对象类型), 既然是对象, 所以也是通过构造函数创建出来的,"所有函数"都是通过Function构造函数创建出来的对象 2.JavaScript中只要是"函数"就有prototype属性 "Function函数"的prototype属性指向"Function原型对象" 3.JavaScript中只要"原型对象"就有constructor属性 "Function原型对象"的constructor指向它对应的构造函数 4.Person构造函数是Function构造函数的实例对象, 所以也有__proto__属性 Person构造函数的__proto__属性指向"Function原型对象" */ function Person(myName, myAge) { this.name = myName; this.age = myAge; } let obj1 = new Person("lnj", 34); // console.log(Function); // console.log(Function.prototype); // console.log(Function.prototype.constructor); // console.log(Function === Function.prototype.constructor); // true // console.log(Person.__proto__); console.log(Person.__proto__ === Function.prototype); // true
通过上面的代码,我们可以得到以下的关系图
Function也是一个函数,那么他也是一个对象,他也有proto属性,但是与其他的有点不同,他的proto属性是指向Function原型对象的.那么就有下面的关系
5.2 object函数
JavaScript中还有一个系统提供的构造函数叫做Object,只要是函数都是"Function构造函数"的实例对象,而通过object构造函数又能创建实例对象,所以有以下关系
代码验证如下
console.log(Function.__proto__); console.log(Function.__proto__ === Function.prototype); // true console.log(Object); console.log(Object.__proto__); console.log(Object.__proto__ === Function.prototype); // true console.log(Object.prototype); console.log(Object.prototype.constructor); console.log(Object.prototype.constructor === Object); // true console.log(Object.prototype.__proto__); // null
我们得到最终的关系图
6.原型链
在了解了上面的对象三角恋之后,再回来看一下以前优化的代码,
Person.prototype = { // 注意点: 为了不破坏原有的关系, 在给prototype赋值的时候, 需要在自定义的对象中手动的添加constructor属性, 手动的指定需要指向谁 constructor: Person,//这样一来就没有破坏原来的关系了 currentType: "人", say: function () { console.log("hello world"); } } let obj1 = new Person("lnj", 34);
-
我们再来看一下对象在查找属性和方法的时候怎么做, 会先在当前对象查找,如果当前对象中找不到想要的, 会依次去上一级原型对象中查找,如果找到Object原型对象都没有找到, 就会报错,如下图
7.属性注意点
分析一下以下代码
function Person(myName, myAge) { this.name = myName; this.age = myAge; } Person.prototype = { constructor: Person, currentType: "人", say: function () { console.log("hello world"); } } let obj = new Person("lnj", 34); // 注意点: 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性 obj.currentType = "新设置的值"; console.log(obj.currentType); // 新设置的值 console.log(obj.__proto__.currentType); // "人"