面向对象不是一种语法,是一种编程的思想。写代码的时候更多的关注点在对象。
js面向对象的特点:封装、继承、多态。
面向对象的核心基础 → 类 → 对象(属性+方法)
对象的本质:属性和方法的集合。
类是对象的抽象,对象是类的实例。
学过的对象:Array、String、Math、Date......
既然是要创造对象,让对象做自己的事情,首先就需要创建对象:
1.字面量创建:var obj = { };
2.构造函数方式创建:var obj = new Object( );
//如果要定义多个人的信息,需要这样创建多次。
var obj1 = new Object( );
obj1.name = "张三";
obj1.age = 12;
obj1.sex = '男';
var obj2 = new Object( );
obj2.name = '李四';
obj2.age = 13;
obj2.sex = '女';
var obj3 = new Object( );
obj3.name = '王五';
obj3.age = 11;
boj3.sex = '女';
......
1.工厂函数创建对象:
function createObj(name,age,sex){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
return obj;
}
var obj1 = createObj("张三",12,"男");
var obj2 = createObj("李四",13,"女");
var obj3 = createObj("王五",11,"女");
优点:可以同时创建多个对象
缺点:创建出来的没有具体的类型(比如是Array和MouseEvent),都是object类型的,但我们看到自己的对象只是object,不知道具体是什么类型。
解决方案:自定义构造函数。其实相当于自己写一个函数,专门用来new对象。
2.自定义构造函数:
//普通函数和构造函数的区别在于调用,可以用来new的就是构造函数。
//一般构造函数首字母习惯大写。
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
var obj1 = new Person("张三",12,"男");
var obj2 = new Person("李四",13,"女");
var obj3 = new Person("王五",11,"女");
构造函数和普通函数不同的地方在于使用new的时候,中间发生了很多看不见的过程:
- 隐式创建了一个新对象,this指向了这个新对象(新对象就有了属性,创建了属性)
- 执行构造函数,也就是调用了这个函数(给对象添加属性和方法,给属性和方法赋值
- 隐式返回这个新对象
构造函数的缺点:
一个构造函数中给对象添加了一个方法,然后创建了两个对象,这两个对象都有这个方法,并且这两个方法一模一样,但是这个方法在内存却是两个空间,这样对于内存空间来说有点浪费,因为两个对象的方法是一样的但是却占了两个空间。
function Person(name){
this.name = name;
this.say = function(){
console.log("说话");
}
}
var obj1 = new Person("张三");
var obj2 = new Person("李四");
console.log(obj1.say == obj2.say); // false 表示这是两个空间
如果将方法定义在对象外面,可以解决这个问题。
function Person(name,age){
this.name = name;
this.age = age;
this.sayHi = fn;
}
function fn(){
console.log("我是" + this.name + ",今年" + this.age + "岁");
}
var zs = new Person("张三",12);
var ls = new Person("李四",13);
但是在后期我们使用插件或者定义别的函数的时候有可能会造成覆盖的现象。所以这个解决方案解决不彻底。
使用原型可以完美解决这个问题。
3.原型:
概念:
任何函数在创建好的时候,浏览器会分配一个对象给这个函数,通过函数的prototype可以访问到这个对象。这个对象叫做原型对象,简称原型。通过new构造函数实例化出来的对象默认可以访问到这个对象的属性和方法。
给这个原型添加属性和方法,使用实例对象可以访问到。
function Person(){
}
Person.prototype.name = '赵六';
Person.prototype.say = function(){
console.log("说话");
}
var obj = new Person();
console.log(obj); // 赵六
obj.say(); // 说话
对象访问属性的时候,先找自己有没有,自己有就直接使用,自己没有就去原型对象上找。
function Person(){
}
Person.prototype.name = '赵六';
Person.prototype.say = function(){
console.log("说话");
}
var obj1 = new Person();
var obj2 = new Person();
console.log(obj1.say == obj2.say); // true
表示这是同一个内存空间,因为两个对象访问的这个方法是Person这个函数对应的原型对象上的方法,两个对象用的是同一个构造函数Person,所以是相等。这样就能解决上面构造函数的问题了。
3.原型链:
对象调用方法或者访问属性的时候,如果自己没有,就去原型对象prototype上找,找到了访问。那如果找不到怎么办?
对象天生就有一个属性叫做__proto__,也叫隐式原型,那么,原型对象也是对象,他也有这个属性,也就是说,原型对象也有原型自己的构造函数和原型对象。
实例对象的__proto__ 指向构造函数的prototype
function Person(){
}
var obj = new Person(); // 通过构造函数创建了对象obj
var proto = obj.__proto__; // 通过实例对象访问到原型对象
console.log(proto);
任何一个原型对象中天生自带一个属性constructor,指向构造函数本身。
继续访问原型对象的原型:
function Person(){
}
var obj = new Person(); // 通过构造函数创建了对象obj
var proto = obj.__proto__; // 通过实例对象访问到原型对象
var proto1 = proto.__proto__; // 访问原型对象的元素
console.log(proto1);
function Person(){
}
var obj = new Person(); // 通过构造函数创建了对象obj
var proto = obj.__proto__; // 通过实例对象访问到原型对象
var proto1 = proto.__proto__; // 访问原型对象的元素
var proto2 = proto1.__proto__; // 访问Object的原型对象的原型
console.log(proto2); // null
对象查找属性的规则:
先在自己身上找,如果有,直接使用;如果没有,顺着原型链往上找,找到了就使用,找不到就继续往上找;原型的顶层原型叫做null,如果找到了null,都没有的话,就返回undefined。
4.instanceof:
判断当前对象是否是另一个对象的实例
语法:某个实例对象 instanceof 某个构造函数
instanceof 可以用来验证数组
var arr = [3,6,2,5]
var obj = {name:'lisi'}
console.log(typeof obj) // object
console.log(typeof arr) // object
console.log(arr instanceof Array) // true
console.log(obj instanceof Array) //false
5.class:
构造函数的语法糖,constructor用来放置属性
class Dog {
constructor(name){
// 当new Dog()的时候这个constructor就会默认执行
this.name = name
}
//这里的say默认就在原型上
say(){
console.log(`My name is ${this.name}`)
}
}
var snoopy = new Dog('Snoopy')
console.log(snoopy)
snoopy.say( )
案例:面向对象打印九九乘法表
function Cfb (n) {
this.n = n
// str 在init和print里面都要用,所以写给this
this.str = ''
// 这里的init和print是每一个实例默认都会调用的方法,可以直接放在构造函数里
this.init()
this.print()
}
Cfb.prototype.init = function () {
// init里的this值将来调用init的对象,也就是将来new的那个对象
for (var i = 1; i <= this.n; i++) {
for (var j = 1; j <= i; j++) {
this.str += `${j} x ${i} = ${i*j} `
}
this.str += '<br>'
}
}
Cfb.prototype.print = function () {
document.write(this.str)
}
new Cfb(9)
new Cfb(19)
class改造乘法表:
class Cfb (n) {
constructor(n){
this.n = n
this.str = ''
this.init()
this.print()
}
init() {
for (var i = 1; i <= this.n; i++) {
for (var j = 1; j <= i; j++) {
this.str += `${j} x ${i} = ${i*j} `
}
this.str += ''
}
print( ) {
document.write(this.str)
}
new Cfb(9)
new Cfb(19)