目录
面向对象
对象
构造函数
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 = 21
obj3.show = function () {
console.log('i am obj3---show')
}
// 3. 通过工厂函数的方式
// (1) 创建一个工厂函数
function factory(name, age) {
// 手动创建对象
var obj4 = {}
obj4.name = name
obj4.age = age
obj4.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 = name
this.age = age
this.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 = name
this.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指向obj9
return 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 = name
this.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); //false
console.log(cat1.print === cat2.print); //true
console.log(Cat.prototype); //{print: ƒ, constructor: ƒ}
// 问题1: cat.__proto__ 指向 Cat.prototype
console.log(cat1.__proto__ ) //{print: ƒ, constructor: ƒ}
console.log(cat1.__proto__ === Cat.prototype); //true
// 问题2: Cat.prototype.__proto__指向 Object.prototype
console.log(Cat.prototype.__proto__ ); // {constructor: ƒ, __defineGetter__: ƒ, …}
console.log(Cat.prototype.__proto__ === Object.prototype); //true
// 问题3: Cat.__ptoto__ 指向 Function.prototype
console.log(Cat.__proto__); //ƒ () { [native code] }
console.log(Cat.__proto__ === Function.prototype); //true
// 问题4: Object.prototype.__proto__ 指向 null
console.log(Object.prototype.__proto__); // null
// 问题5: Object.__proto__指向 Function.prototype
console.log(Object.__proto__ ); // ƒ () { [native code] }
console.log(Object.__proto__ === Function.prototype); // true
// 问题6: Function.prototype.__proto__ 指向 Object.prototype
console.log(Function.prototype.__proto__); //{constructor: ƒ, __defineGetter__: ƒ, …}
console.log(Function.prototype.__proto__ === Object.prototype); //true
// 问题7: Function.__proto__ 指向 Object.prototype
console.log(Function.__proto__ ); // ƒ () { [native code] }
console.log(Function.__proto__ === Function.prototype); //true
console.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()); //100
console.log(obj1.getA.apply(obj2)); //300 使得obj1.getA中的this指向obj2
// 添加原型上的属性
Object.prototype.c = 600
// hasOwnProperty() 检测是否属于自身属性
console.log(obj1.hasOwnProperty('a')); // true
console.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