abstrict
JS中没有传统的类,使用构造函数模拟
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上,返回 true / false
this指向所创建的类 ,因此可以使用this.属性名,创建属性
构造函数会自动 return 返回
const Animals = function (animal, food) {
console.log(this); //Animals()
this.animal = animal; //属性名可以随意定义
this.food = food;
};
const cat = new Animals("Cat", "fish");
const dog = "Dog";
console.log(cat instanceof Animals); //true
console.log(dog instanceof Animals); //false
Way 2 :
class Animals {
constructor(animal, food, birthYear) {
this.animal = animal;
this.food = food;
this.birthYear = birthYear;
}
calAge() {
console.log(2023 - this.birthYear);
}
}
const dog = new Animals("dog", "bone", 2020);
dog.calAge(); //3
链接protoType
console.log(Animals.prototype);
Animals.prototype.likes = function (likes) {
console.log("wool");
};
console.log(cat.__proto__);
console.log(cat.__proto__ === Animals.prototype); //true
console.log(Animals.prototype.isPrototypeOf(cat)); //true
console.log(Animals.prototype.isPrototypeOf(dog)); //false
console.log(cat.hasOwnProperty("animal")); //true
set 与 get
调用原型数据,进行阻挡操作
const account = {
owner: "Chris",
movements: [200, 530, 120, 300],
// number: 10,
get latest() {
return this.movements;
},
set changeNum(num) {
return num > 10 ? alert("Good") : alert("Bad");
},
};
console.log(account.latest);
account.changeNum = 99;
// DEMO 2
// 定义一个对象构造函数
function Person(name) {
this.name = name;
}
// 在原型中定义属性 age,并为其设置 get 和 set 函数
Object.defineProperty(Person.prototype, "age", {
get: function () {
return this._age;
},
set: function (value) {
if (value >= 0 && value <= 120) {
this._age = value;
} else {
console.log("Invalid age value");
}
}
});
// 创建对象实例
var person = new Person("John");
// 设置年龄属性
person.age = 30;
// 获取年龄属性
console.log(person.age);
// DEMO 3
var obj = {
_name: '',
get name() {
return this._name
},
set name(value) {
this._name = value.toUpperCase()
}
}
obj.name = 'Chris'
console.log(obj.name);
Object.create
用于创建一个新对象,该对象的原型链由指定的原型对象和属性和方法描述符组成。
// Object 创建
const Person = {
init(name, birthYear) {
this.name = name;
this.birthYear = birthYear;
},
calAge(birthYear) {
// console.log(2023 - this.birthYear);
return 2023 - this.birthYear;
},
};
console.log('查看原型');
console.log(Person);
const Chris = Object.create(Person);
// Chris.name = "Chris";
// Chris.birthYear = 2001;
Chris.init("Chris", 2001);
console.log('查看Chris原型');
console.log(Chris);
console.log(Chris.calAge());
// Chris.calAge();
console.log(Chris.__proto__ === Person);
// DEMO 2
// 创建一个空对象 obj,它的原型为 null
var obj = Object.create(null);
// 创建一个对象 person,它的原型为 Object.prototype
var person = Object.create(Object.prototype);
// 创建一个对象 student,它的原型为 person 对象
var student = Object.create(person);
// 在 student 对象上定义属性和方法
student.name = "John";
student.age = 25;
student.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
// 调用 student 对象的方法
student.sayHello(); // 输出 "Hello, my name is John"
Object.create与new区别
new
使用 new 运算符调用构造函数时,会创建一个新的对象,并将构造函数中的 this 关键字绑定到新对象上,同时,新对象会继承构造函数原型上的属性和方法
new 运算符创建的对象会通过原型链继承构造函数的原型上的属性和方法。每个对象都有一个隐藏的属性 proto,指向其构造函数的原型。如 john.proto 指向 Person.prototype。这样可以访问构造函数原型上的属性和方法。
function Person(name, age) {
this.name = name;
this.age = age;
}
var john = new Person("John", 30);
Object.create()
在给定的原型对象的基础上创建一个新对象。它接受一个参数,用于指定对象的原型
Object.create() 方法创建的对象也会通过原型链继承原型对象的属性和方法,但不会与构造函数或原始对象有直接的联系。例如,john.proto 指向 personPrototype。
var personPrototype = {
greet: function() {
console.log("Hello!");
}
};
var john = Object.create(personPrototype);
继承
关注原型链是否被更改,当更改后,初始原型链的方法会全部丢失
解决方案: 再修改原型链后,重新添加方法,但如果父类有相同函数名称,则会以子类为主
const Person = function (firstName, birthYear) {
this.firstName = firstName;
this.birthYear = birthYear;
};
Person.prototype.calcAge = function () {
console.log(2037 - this.birthYear);
};
// 子类
const Student = function (firstName, birthYear, course) {
// console.log(this);
// this 实现继承
Person.call(this, firstName, birthYear);
this.course = course;
};
Student.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I study ${this.course}`);
};
// 1. 解决不能调用introduce方法,在分配后再创建新属性
// 2. 分配前调用,原型链被修改,instroduce不在存在
Student.prototype = Object.create(Person.prototype);
// Student.prototype.constructor = Student;
// Student.prototype.introduce = function () {
// console.log(`My name is ${this.firstName} and I study ${this.course}`);
// };
// 未修改前原型链
// const Merry = new Student("Merry", 2001, "Computer");
// Merry.introduce()
// 连接原型,将calcAge附加到Student中,
const Chris = new Student("Chris", 2001, "Computer");
console.log(Chris);
console.log('修改后的原型指向--------------------');
Chris.calcAge();
// introduce调用失败,因为已经不再原型上
// Chris.introduce()
// console.log(Person.prototype);
// console.log(Student.prototype);
//全部继承
// console.log(Chris instanceof Student);
// console.log(Chris instanceof Person);
// console.log(Chris instanceof Object);
// 修改原型链指向
// console.log(Student.prototype.constructor); //Person,因为Student的原型链指向为Person
Student.prototype.constructor = Student;
console.log(Student.prototype.constructor); //Student
// DEMO 2
class first {
constructor(name) {
this.name = name
}
}
class second {
constructor(name) {
this.name = name
}
}
const firstPro = new first('Tom')
second = firstPro //原型链断开,直接赋值,导致原型链混乱,数据会同时更改
second.name = 'Chris'
console.log(firstPro);
console.log(second);
// DEMO 3
// 父类
function Animal(name) {
this.name = name;
}
Animal.prototype.sound = function () {
console.log("Animal sound");
};
// 子类
function Cat(name) {
Animal.call(this, name); // 调用父类构造函数
}
// 设置原型继承
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
// 子类方法
Cat.prototype.sound = function () {
console.log("Meow"); // 子类的声音
};
// 创建实例
var myCat = new Cat("Tom");
console.log(myCat.name); // 输出: Tom
myCat.sound(); // 输出: Meow
super
用于调用父类的构造函数、普通方法、静态方法。它通常用于子类继承父类时使用
在构造函数中,调用super会执行父类的构造函数,从而完成对父类属性的初始化。如果子类在调用super之前使用this关键字,那么此时父类的属性还未被初始化,会导致错误。
在静态方法中,由于静态方法是类级别的方法,而非实例级别的,因此无法通过实例调用父类的静态方法。所以,需要使用super关键字来调用父类的静态方法。
// 用于调用父类的构造函数、普通方法、静态方法。它通常用于子类继承父类时使用。
class Animals {
constructor(name) {
this.name = name
}
sayHello() {
console.log('Hello World');
}
static Hi() {
console.log("Hi !!!");
}
}
class dog extends Animals {
constructor(name, toys) {
super(name)
this.toys = toys
}
sayGoodBye() {
super.sayHello()
console.log("GoodBye World");
}
static Holo() {
super.Hi()
console.log('Holo !! ');
}
}
const d = new dog('lili', 'Bone')
console.log(d);
d.sayGoodBye()
// 类名调用静态类
dog.Holo()
类继承与super使用
class PersonCl {
constructor(fullName, birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
// static method
static hey() {
console.log("Hey World 👨👨👦");
}
}
// 继承类
class studentCl extends PersonCl {
constructor(fullName, birthYear, course) {
super(fullName, birthYear);
this.course = course;
}
introduce() {
console.log(`My name is ${this.fullName} and I study ${this.course}`);
}
}
const Chris = new studentCl("Chris", 2001, "Computer Science");
Chris.introduce();
封装
# == private,对变量or方法进行封装
外部调用私有方法:
-
浏览器报错 : Private field ‘#calAge’ must be declared in an enclosing class
-
vaCode报错 : Property ‘#calAge’ is not accessible outside class ‘Animals’ because it has a private identifier.
class Animals {
// public fields
local = navigator.language;
// private fields
#toys = [];
constructor(name, birthYear, food) {
this.birthYear = birthYear;
this.food = food;
this.name = name;
}
// public methods
calToys(toy) {
this.#toys.push(toy);
return this;
}
// private methods
#calAge(birthYear) {
this.birthYear = 2023 - birthYear;
}
outAge(birthYear) {
this.#calAge(birthYear);
}
}
const dog = new Animals("dog", 2012, "bone");
dog.calToys("tennis");
dog.calToys("flying disc")
console.log(dog.calToys("flying disc"));
// // console.log("private methods" + dog.#calAge); //报错
// console.log(dog);
chaining链接方法
需要对与每一个方法返回this指向,否则报错,Cannot read properties of undefined
// public methods
calToys(toy) {
this.#toys.push(toy);
return this;
}
dog.calToys("shoes").outAge(2020); // 3