目录
面向对象
对象
构造函数
this关键字
原型
- 面向对象
面向过程: 在开发过程中,关注过程的开发方式. 在开发时关注每一个细节,步骤和顺序.
面向对象: 在开发过程中,只需要找一个对象来完成事情的开发思想
对象: 在生活中,万物皆对象
封装: 将完成步骤封装在对象内部
属性: 对象的特征
核心: ①制造一个对象, 批量生产对象; ②每个对象都有自己的属性(特征)和方法(函数,行为); ③每个对象都类似,但不相同(构造函数)
e.g:
(1)想吃饭
面向过程:
-> 所有事情自己亲力亲为
-> 考虑顺序: 烧水, 洗菜, 切菜, 炒菜...
-> 考虑细节: 切菜时,菜切的花样
面向对象:
-> 只需要到饭店去点菜 (不关注菜如何做, 找一个做饭的对象)
(2)开发选项卡
面向过程:
-> 考虑获取哪些元素? tab标签, 每一个tab标签对应的内容区域
-> 考虑给哪些元素绑定点击事件? tab标签绑定点击事件
-> 考虑点击的功能? 进行内容的切换
面向对象:
-> 找到一个能完成选项卡功能的对象, 让这个对象来完成
-> 注意: 这个对象是否存在? 存在则直接使用, 不存在则制造对象(创建对象)
制造对象:
-> 我们需要一个制造对象的机器(抽象的概念)
-> 机器的作用是: 生产一个对象(具体的概念)
-> 生产出来的对象要能够完成选项卡的功能
-> 因此, 我们只需要准备这个机器
-> 开发过程中, 只需要造机器, 由机器生产出来的一个对象来完成选项卡功能, 当开发另一个选项卡时, 不需再次制造机器, 只需要该机器再生产一个对象来完成另一个选项卡功能即可
- 对象
对象的创建方式
字面量方式,可动态添加属性和方法, 但无法批量生产对象
Object 内置构造函数的方式 可动态添加属性和方法, 但添加属性和方法时无法批量添加
通过工厂函数的方式
自定义构造函数的方式
// 创建对象方式// 1. 通过字面量的方式 可动态添加属性和方法, 但无法批量生产对象var obj1 = {name: 'mm',age: 18,show: function () {console.log('i am obj1---show')},}var obj2 = {name: 'tt',age: 20,show: function () {console.log('i am obj2---show')},}// 可动态添加属性和方法obj1.sex = '女'obj2.sex = '男'obj1.run = function () {console.log('i can run!!!!!---obj1')}obj2.run = function () {console.log('i can run!!!!!---obj2')}// 2. Object 内置构造函数的方式 可动态添加属性和方法, 但添加属性和方法时无法批量添加var obj3 = new Object()obj3.name = 'qq'obj3.age = 21obj3.show = function () {console.log('i am obj3---show')}// 3. 通过工厂函数的方式// (1) 创建一个工厂函数function factory(name, age) {// 手动创建对象var obj4 = {}obj4.name = nameobj4.age = ageobj4.show = function () {console.log('i am obj4---show')}// 手动返回对象return obj4}// (2) 使用工厂函数创建对象var obj5 = factory("aa", 18)var obj6 = factory("zz", 19)var obj7 = new Factory()console.log('5---',obj5);console.log('6---',obj6);console.log(obj5 == obj6); //false 两个对象类似但不相同// 4. 自定义构造函数的方式// (1) 自定义函数就是在造机器// (2) 构造函数会自动的创建并返回对象,无需return(创建对象时,需要与new关键字配合使用)// (3) 手动添加属性和方法function Factory(name,age){// 手动向对象中添加属性和方法// this指当前对象, 动态变化, 指向调用者this.name = namethis.age = agethis.show = function(){console.log('i am' + obj + '---show');}}// 创建对象 new 构造函数名() 首字母大写,为了区分构造函数var obj7 = new Factory("ww",17)var obj8 = new Factory("ss",16)console.log('7---',obj7);console.log('8---',obj8);// 访问属性console.log(obj7.name); // ww
创建对象方法比较
将方法写在构造函数内部:
将方法写在构造函数的原型上:
- 构造函数
构造函数
能批量生产对象
可像函数一样传递参数, 可以为每一个对象添加不同的内容
当需要完成一个功能时, 造一个构造函数, 利用构造函数来创建完成功能的对象
'机器'即为构造函数, 属性就直接写在构造函数内部,方法写在构造函数的原型上(避免每创建一次对象就创建一次相同的函数,造成空间的浪费)开发过程,即为造'机器'的过程, 就是面向对象的封装过程
// 创建对象 Date是一个制造机器, new Date()创建一个对象, 使用对象来获取年份,月份等var time1 = new Date()var time2 = new Date()console.log(time1 == time2); //false 每个对象类似,但不相同var point = { x:10,y: 20,function(){console.log('point---function');}}console.log(point.x);
普通函数和自定义构造函数的比较
普通函数: 使用时函数调用[函数名()], 函数名小驼峰式命名规则
自定义构造函数: 用来创建对象[new 函数名()],使用时必须与new连用,否则不能自动创建对象,函数名大驼峰式命名规则(所有单词首字母大写, 与普通函数做区分)
使用构造函数的问题
问题: 如果将方法写在构造函数的内部, 在每次创建对象时,都会创建函数,造成空间浪费
解决: 方法只创建一次, 供所有对象使用, 使用原型
// 自定义Dog的构造函数function Dog(name, type) {// 定义属性this.name = namethis.type = type// 定义方法this.print = function () {console.log('dogName: ', this.name, ', type:', this.type)}}// 创建对象(生产实例)var dog1 = new Dog('小白', '萨摩耶')var dog2 = new Dog('小黑', '二哈')console.log('dog1: ',dog1, 'dog2: ', dog2);// 调用print方法dog1.print()dog2.print()// dog1和dog2不是相同的对象console.log(dog1 === dog2); //false// dog1和dog2的print方法不同, 是两个函数console.log(dog1.print === dog2.print); //false/* dog1 第一次使用new Dog创建的对象 会将this指向dog1, 将构造函数中的代码全部执行一遍(在dog1上添加name和type属性, 同时也在dog1上添加print方法,创建一个函数)dog2 第二次使用new Dog创建的对象 会将this指向dog2, 将构造函数中的代码全部执行一遍(在dog2上添加name和type属性, 同时也在dog1上添加print方法,创建一个函数) */
- this关键字
this指向问题
普通函数函数中,this指向函数调用者
this指向window对象(全局)
箭头函数中,this指向函数定义时this的指向(通常函数定义时指向的是window)
// this指向问题// 1. this指向当前调用者var obj9 = {a: 10,b: 20,show: function(){console.log(this); // this指向obj9return this.a }}console.log(obj9.show()); //10// 2. this指向 Window 对象 (当前调用者为window)var a = 30 //全局变量var show1 = obj9.show //不加() 获取到show函数console.log(show1); show1() //等价于window.show(), show中的this指向全局的Window对象, 返回的是全局变量a=30的值
改变this指向
call() 方法 fun.call(thisArg, arg1, arg2, ...) 调用一个对象(可调用函数)
apply()方法 fun.apply(thisArg, [argsArray]) 可调用函数, 可改变函数的 this 指向, 参数必须为数组形式 [argsArray]
bind()方法 fun.bind(thisArg, arg1, arg2, ...)不会调用函数, 可以改变函数内部this指向
- 原型
原型
在js中的每一个函数都有一个属性 prototype(原型对象),是一个对象
每个对象上都有一个 __proto__(对象原型)属性, 指向所属构造函数的prototype
当访问对象的属性或方法时, 首先在自身查找, 若没有,则会在 __proto__上查找
在原型上添加方法, 在内存中只会创建一个方法, 专门供对象使用
构造函数constructor: 本身也是函数,故也有prototype属性
/* 在js内部,所有的object对象数据类型都属于内置构造函数Object在js内部,所有函数都属于内置构造函数Function对象 *//* 问题1: cat对象上的__proto__指向谁?-> cat 所属的构造函数是 Cat-> cat.__proto__ 指向 Cat.prototype问题2: Cat.prototype上的__proto__指向谁?-> Cat.prototype 是一个对象, 也有__proto__属性-> Cat.prototype所属的构造函数是Object-> Cat.prototype.__proto__指向 Object.prototype问题3: Cat的__proto__指向谁? -> Cat是一个自定义构造函数, 本事也是一个对象, 故也有__proto__属性-> Cat 的所属构造函数是 Function (在js内部,所有函数都属于内置构造函数Function对象)-> Cat.__ptoto__ 指向 Function.prototype问题4: Object.prototype上的__proto__指向谁?-> Object.prototype 是一个对象, 也有__proto__属性-> Object.prototype在js中是顶级原型对象,不存在__proto__对象原型-> Object.prototype.__proto__ 指向 null问题5: Object上的__proto__指向谁?-> Object对象有__proto__属性-> Object是一个内置构造函数, 故所属构造函数是 Function-> Object.__proto__指向 Function.prototype问题6: Function.prototype上的__proto__指向谁?-> Function.prototype 是一个对象, 也有__proto__属性-> Function.prototype 所属构造函数是 Object-> Function.prototype.__proto__ 指向 Object.prototype问题7: Function上的__proto__指向谁?-> Function 是一个对象, 也有__proto__属性-> Function 是一个内置构造函数, 所属的构造函数就是Function -> Function.__proto__ 指向 Function.prototype*/function Cat(name, type) {// 定义属性this.name = namethis.type = type}// Cat.prototype会得到一个对象, 是Cat构造函数的原型Cat.prototype.print = function(){console.log('CatName: ', this.name, ', Cattype:', this.type)}var cat1 = new Cat('小白', '小橘猫')var cat2 = new Cat('小黑', '英短')console.log(cat1, cat2);console.log(cat1 === cat2); //falseconsole.log(cat1.print === cat2.print); //trueconsole.log(Cat.prototype); //{print: ƒ, constructor: ƒ}// 问题1: cat.__proto__ 指向 Cat.prototypeconsole.log(cat1.__proto__ ) //{print: ƒ, constructor: ƒ}console.log(cat1.__proto__ === Cat.prototype); //true// 问题2: Cat.prototype.__proto__指向 Object.prototypeconsole.log(Cat.prototype.__proto__ ); // {constructor: ƒ, __defineGetter__: ƒ, …}console.log(Cat.prototype.__proto__ === Object.prototype); //true// 问题3: Cat.__ptoto__ 指向 Function.prototypeconsole.log(Cat.__proto__); //ƒ () { [native code] }console.log(Cat.__proto__ === Function.prototype); //true// 问题4: Object.prototype.__proto__ 指向 nullconsole.log(Object.prototype.__proto__); // null// 问题5: Object.__proto__指向 Function.prototypeconsole.log(Object.__proto__ ); // ƒ () { [native code] }console.log(Object.__proto__ === Function.prototype); // true// 问题6: Function.prototype.__proto__ 指向 Object.prototypeconsole.log(Function.prototype.__proto__); //{constructor: ƒ, __defineGetter__: ƒ, …}console.log(Function.prototype.__proto__ === Object.prototype); //true// 问题7: Function.__proto__ 指向 Object.prototypeconsole.log(Function.__proto__ ); // ƒ () { [native code] }console.log(Function.__proto__ === Function.prototype); //trueconsole.log( function(){} instanceof Object); //true 函数也是一个对象console.log(Object instanceof Object); //true 函数也是一个对象
原型链
原型链是用__proto__属性穿起来的链状结构
原型上的属性
var obj1 = {a: 100,b: 200,getA :function() {return this.a},}var obj2 = {a: 300,b: 400,getA :function() {return this.a},}console.log(obj1.getA()); //100console.log(obj1.getA.apply(obj2)); //300 使得obj1.getA中的this指向obj2 // 添加原型上的属性Object.prototype.c = 600 // hasOwnProperty() 检测是否属于自身属性console.log(obj1.hasOwnProperty('a')); // trueconsole.log(obj1.hasOwnProperty('c')); // false c属性在原型上,不属于自身属性// 遍历对象 // 可获取到原型上的属性值for(var key in obj1){console.log(obj1[key]); // 100 200 ƒ(){return this.a } 600 }// Object.keys() 无法获取到原型上的属性console.log(Object.keys(obj1)); //['a', 'b', 'getA']
PreviousNotes:
https://blog.csdn.net/qq_54379580/article/details/126464151