day0425
类
类:模具
对象:类的实例
实例化对象,可以写小括号,也可以不写小括号,如果实例化时构造函数有参数,则必须写
写小括号优先级更高20,不写小括号优先级低一点19
JavaScript运算符优先级汇总表:
构造函数和普通函数的区别
ES6中的类
// es6中提供的一个语法糖 class 它可以方便我们去完成类的创建
// 类
class Myclass {
// 成员属性,定义并赋值 访问权限的私有
__age = 20
// 构造方法
constructor(id, name = '李四') {
// 成员属性,属性值通过构造方法得到
this.id = 100
this.name = '张三'
}
/* getAge() {
return this.__age + '岁'
}
setAge(num) {
this.__age = Math.max(1, Math.min(200, num))
} */
get age() {
return this.__age + '岁'
}
set age(num) {
this.__age = Math.max(1, Math.min(200, num))
}
// 方法
fn() {
console.log('fn -=- 我是类中的一个方法');
console.log(this);
}
}
// 匿名类
const Myclass = class {
hello(){
console.log('hello');
}
}
const my = new Myclass
my.hello()
ES5中的类
// 它就是一个类 (如果函数成为了类,则此函数叫构造函数) == 函数
function Myclass(a, b) {
// 私有变量 函数的定义方式
// var c = 2000
// 如果把此函数当前构造函数
// this.c = 3000
console.log(this, a, b)
// return 1
return {
id: 1
}
}
// console.log(Myclass(1, 2))
console.log(new Myclass(10, 20))
原型与原型链
function Fn() { };
Fn.prototype.a = function () { };
const o = new Fn;
// 方法返回指定对象的原型 ( 即:内部[prototype]属性)
console.log(Fn.prototype)
console.log(o.__proto__)
console.log(Object.getPrototypeOf(o))
//====================================
let arr = [];
console.log(arr.__proto__ == Array.prototype); // true
let str = "";
console.log(str.__proto__ == String.prototype); // true
私有方法和原型方法同时存
function Fn() {
this.x = 10
this.y = 20
this.getX = function () {
console.log(this.x)
}
}
Fn.prototype.getX = function () {
console.log('prototype == ' + this.x)
}
Fn.prototype.getY = function () {
console.log(this.y)
}
let f1 = new Fn()
f1.getX() // 10
获取与设置原型
// 设置一个指定的对象的原型
Object.setPrototypeOf(对象,父对象)
// 指定对象的原型
Object.getPrototypeOf(对象)
//==========================================
let child = {};
let parent = { id: 100 };
// child.__proto__ = parent;
// 设置child.__proto__的原型指向到parent
Object.setPrototypeOf(child, parent);
console.log(child);
// console.log(child.__proto__)
// 获取child对象的原型
console.log(Object.getPrototypeOf(child));
原型重定向
function Fn() { }
Fn.prototype = {
// 手动设置的constructor
constructor: Fn,
getX() { },
getY() { }
}
优点:
把原型上为其,实例提供的公共属性和方法,全部写在一起了,提高整体性后者模块性
缺点
重定向后的原型对象中,缺失了constructor属性,需要通过手动添加constructor
// ======================== 如果原类中就定义了prototype方法,上面写法会存在方法丢失
function Fn() { }
Fn.prototype = Object.assign(Fn.prototype, {
getX() { },
getY() { }
})
console.log(Fn.prototype)
原型重定向会引发constructor丢失
设置对象属性不可枚举
// 给对象添加一个属性,并且给属性添加一些描述, 不可枚举
Object.defineProperty(Fn.prototype, 'constructor', {
// 指定值
value: Fn,
// 不可枚举
enumerable: false
})
原型检测
instanceof 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
function A() {}
function B() {}
function C() {}
const c = new C();
B.prototype = c;
const b = new B();
A.prototype = b;
const a = new A();
console.dir(a instanceof A); //true
console.dir(a instanceof B); //true
console.dir(a instanceof C); //true
console.dir(b instanceof C); //true
console.dir(c instanceof A); //false
// 使用isPrototypeOf检测一个对象是否是另一个对象的原型链中
const a = {};
const b = {};
const c = {};
Object.setPrototypeOf(a, b);
Object.setPrototypeOf(b, c);
console.log(b.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(b)); //true
原型扩展方法
Array.prototype.unique = function () {
let arr = [];
for (let i = 0; i < this.length; i++) {
if (this.indexOf(this[i]) == i) {
arr.push(this[i])
}
}
return arr
};
Array.prototype.sum = function () {
let total = 0;
for (let i = 0; i < this.length; i++) {
total += this[i]
}
return total;
};
Array.prototype.max = function () {
return Math.max.call(null, ...this)
};
// 给字符串添加几个原型方法
// query id=1&name=lisi
// trim 去空格
// parse {id:1,name=’lisi’} id=1&name=lisi
继承
JS本身是基于面向对象开发的编程语言
封装:类也是一个函数,把实现一个功能的代码进行封装,以此实现“低耦合高内聚”
多态:
重写: 子类重写父类上的方法(伴随着继承运行的)
重载: 相同的方法,由于参数或者返回值不同,具备了不同的功能(js中不具备严格意义上的重载)
继承: 子类继承父类中的方法和属性
JS中继承方案:
1.原型继承 (让子类的原型 = 父类实例)
2.call继承 (只能继承父类中私有的,不能继承父类中公共的)
3.寄生组合式继承 (call继承 + 原型继承)
(4.混合继承,实现多继承)
1.原型继承
// 第1种: 原型继承 (让子类的原型 = 父类实例)
function Parent() {
this.x = 100
}
Parent.prototype.getX = function () {
return 'getX方法中输出:' + this.x
}
function Child() {
this.y = 200
}
// 原型继承
Child.prototype.__proto__ = new Parent;
Child.prototype.getY = function () {
return 'getY方法中的方法输出:' + this.y
}
// 父类中的私有属性都变成了子类公有的
let c1 = new Child
console.log(c1)
2.call继承
function Parent() {
this.x = 100
}
Parent.prototype.getX = function () {
return 'getX方法中输出:' + this.x
}
function Child() {
// call继承
// 在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的哪些东西也就和它没有关系了)
Parent.call(this)
this.y = 200
}
Child.prototype.getY = function () {
return 'getY方法中的方法输出:' + this.y
}
let c1 = new Child
console.log(c1)
3.寄生组合式继承
function Parent() {
this.x = 100
}
Parent.prototype.getX = function () {
return 'getX方法中输出:' + this.x
}
function Child() {
// call继承
Parent.call(this)
this.y = 200
}
// Child.prototype.__proto__ = Parent.prototype
// Object.create 创建一个空对象,让其原型链指向obj
Child.prototype = Object.create(Parent.prototype)
// 指定一下constructor
Child.prototype.constructor = Child
Child.prototype.getY = function () {
return 'getY方法中的方法输出:' + this.y
}
let c1 = new Child
console.log(c1)
4.混合继承
JS不能实现多继承,如果要使用多个类的方法时可以使用mixin混合模式来完成。
function A(name) {
this.name = name;
}
A.prototype.show = function () {
console.log(this.name);
};
const B = {
total() {
console.log("统计下输出");
}
};
const C = {
ajax() {
console.log("发送请求");
}
};
function User(name) {
A.call(this, name);
}
User.prototype = Object.create(A.prototype)
User.prototype.constructor = User
Object.assign(User.prototype, B, C);
let u = new User("张三");
u.show();
u.total();
u.ajax();
重写new
function Fn() {
// 创建一个实例对象
// this指向实例对象
// 也会像普通函数执行一样,让其执行,只不过this指向实例对象
// 返回值没有或基本值,则返回实例对象,如果引用值,以自己的为主
}
let f1 = new Fn()
// ===============================================================
function _new(Construct, ...args) {
// 创建一个实例对象(创建Construct类的实例,让其 对象.__proto__ = Construct.prototype)
// obj.__proto__ = Construct.prototype
let obj = Object.create(Construct.prototype)
// 把函数执行,让this指向实例对象
let ret = Construct.call(obj, ...args)
// 处理返回值,引用类型,直接返回引用类型的值
if (ret !== null && /^(object|function)$/.test(typeof ret)) {
return ret
}
return obj
}
function Fn(name) {
this.name = name
this.age = function () {
console.log('方法===' + this.name)
}
}
let f1 = _new(Fn, '张三')
f1.age()s
重写call和bind
重写call:
Function.prototype.myCall = function (ctx, ...params) {
// 参数可以为 undefined或null
ctx = ctx == null ? window : ctx
// 需要保证ctx必须是对象类型的值:因为只有对象才能设置属性
ctx = !/^(object|function)$/.test(typeof ctx) ? Object(ctx) : ctx
let self = this
let ret = null
// 新增的属性名保证唯一性,防止污染原始对象中的成员数据
let functionName = Symbol('functionName')
// 给对象添加属性
ctx[functionName] = self
// 执行方法
ret = ctx[functionName](...params)
// 删除自定义属性
delete ctx[functionName]
return ret
};
function fn(x, y) {
console.log(this, x, y)
}
let obj = {
name: '张三'
}
fn.myCall(obj, 2, 3)
重写bind:
Function.prototype.myBind = function myBind(ctx, ...params) {
let self = this
return function (...args) {
self.apply(ctx, [...params, ...args])
}
};
var obj = { id: 1 }
function fn(...args) {
console.log(this, args)
}
btn.onclick = fn.bind(obj, 1, 2)
重写instanceof
注:不能检测基本数据类型,检测的实例必须是对象
function _instanceof(obj, FC) {
if (typeof FC !== "function") {
// 抛异常
throw new Error('类型不能,无法使用')
}
if (obj == null) return false
// 检查是否有Symbol.hasInstance属性
if (typeof Symbol !== "undefined") {
let hasIns = FC[Symbol.hasInstance]
if (typeof hasIns === 'function') {
// 执行返回true/false
return hasIns.call(FC, obj)
}
}
// 不支持Symbol
// 获取类的原型
let prototype = FC.prototype
// 获取实例的原型链
let proto = Object.getPrototypeOf(obj)
// 如果类没有prototype则直接返回false
if (!prototype) return false
while (1) {
// 找到原型链最顶,还没有找到返回false
if (proto === null) return false
// 在原型上找到返回true
if (proto === prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
let res = _instanceof(1, Array)
console.log(res)