面向对象编程
- 单例模式:单例模式就是一个普通对象
- 高级单例模式:高级单例模式就是一个函数返回了一个对象;比普通高级在于可以有自己的私有变量
- 工厂模式:就是一个普通函数批量产生一些单例
- 构造函数:通过 new 执行一个函数的时候会先开辟一个堆内存,然后把函数中的 this 指向这个堆内存,执行过程跟普通函数一样,执行完成之后,若 return 后是个引用数据类型,则 new 执行的返回结果就是 return 后面的内容,否则就是这个堆内存
<script> function Person(name,age,sex){ this.name = name; this.age = age; this.sex = sex; } var person = new Person("小明",12,0); console.log(person); - prototype 原型:原型里面存放的都是这个类的共有属性
- 类:就是在 js 中的一些函数,只不过是通过 new 去执行
- 实例:就是通过 new 执行哪个函数得到的结果
- 原型是一个函数的固有属性,只有当使用 new 去执行这个函数的时候原型才能体现他的这个原型中的属性,是当前函数的所有实例的公用属性
- 所有函数的默认原型(固有属性)都会有一个属性 constructor 属性就是函数本身
- 所有的实例都会有一个属性__proto__;属性值是当前实例所属类的 prototype(原型)
- 实例去调用某些属性的时候,先去看看自己有没有这个私有属性,有的话就是用自己私有的,没有的话就去所属的原型上查找;(通过__proto__)找所属类的原型)
- 类的默认原型是 Object 类(基类)的实例,类的默认原型的__proto__是 Object 的 prototype
- 原型链是属性的查找机制:先在自己身上找这个属性,没有的话通过__proto__往所属类的原型上找,这个原型上没有的话,就接着通过这个原型的__proto__当前原型所属类的原型上去找,一直找到基类的原型。
- 类的原型上存放的属性,都是提供 Student 的实例去使用的公用属性
- new 的执行过程:先开作用域,开了一个堆内存,让函数中的 this 指向这个堆,然后形参赋值变量提升,然后代码从上到下执行
- return 的问题:return 后边跟一个引用类型则返回的就是写的这个引用类型,否则返回时这个堆内存(this-->实例)
- this 专有名词叫执行主体
- 严格模式"use strict"下 this 不指定就是 undefined
- 全局作用域下 var 或 function 出来的变量会同时给 window 这个对象增加对应的属性
- 函数都用 prototype 里面用来存储实力能够使用的共有属性,自带的(默认)的 prototype 上都有 constructor:指的是这个构造函数本身,所有的实例都有一个__proto__指的是所有属性类的 prototype
- 实例.xxx:先在自己身上查找这个属性,没有的话,通过__prot__向上层对象 A 查找,上层对象 A 若也没有这个属性,则接着通过__proto__向 A 的上层对象 B 中查找。一直找到基类(Object 类)de 原型(prototype),这时都没有对应的属性时,结果就是 undefined,因为基类的 prototype 的__proto__是 null,也就是说基类的 prototype 是最顶层
- constructor 这个属性之所以能用来判断数据类型,是因为实例调用了 constructor 都是调用的原型上的 constructor,指向就是构造函数本身
- A instanceof B:从 A 到基类的原型,这条原型链上有没有 B 的原型存在;
特殊:只能用于引用数据类型,值类型不适用;
- 使用 hasOwnproperty 可以判断属性是否是私有的:返回值是 true 就是私有的,是 false 就是公有的
p1.hasOwnproperty(k)//封装一个函数判断元素是否在公有属性上与hasOwnProperty作用相同 function P1(name, age) { this.name = name; this.age = age; } let p1 = new P1("one", 24); P1.prototype.tos = "12"; Object.prototype.hasPubProperty = function (key) { return (key in this) && !this.hasOwnProperty(key); } - 所有的 this.xxx 的属性都是当前实例的私有属性
- 一般我们会把方法类的属性放到原型上
- 约定的规范:原型上的方法中的 this 保证是当前类的实例
- new 的执行过程:先开辟一个作用域,然后开辟一个堆内存,this 指向这个堆,形参赋值变量提升,代码执行。
new 返回值:不是引用的话就是默认返回 this
- call 执行函数并且改变函数 this 指向
f.call(xxx,a,b,c)//a,b,c是传给f的实参,call 中的第一个参数是用来修改函数中的 this 指向,call 从第二个参数开始就是传给函数的实参 - 实现构造函数 new 方法
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function myName(C, ...ages) {
//new的特性是开辟一个新堆空间
let obj = {};
//让实例指向他的构造函数
obj.__proto__ = C.prototype;
//改变this指向指向他开辟的空间obj
let a = C.call(obj, ...ages);
//判断构造函数执行结构,如果是引用类型就返回它执行结构,如果是值类型就返回this
return typeof a == "object" ? a : obj;
}
let p1 = new Person("小康", 12, 1);
let p2 = myName(Person, "小康", 12, 1)
- 手写一个 instanceof 方法
var ary = [];
function myInstance(a, b) {
//判断a.prototype是否存在并且a.__proto__ !== b.prototype
while (a.__proto__&&a.__proto__ !== b.prototype) {
a = a.__proto__;
if (a.__proto__ === null) {
return console.log("没有")
}
}
return true;
}
console.log(myInstance(ary, Array));
console.log(myInstance(ary, Object));
- 运算符优先级(从高到底)
- 将类数组转化为数组的方法
- 用...展开符
[...arguments] - 结合 call 来实现:
[].slice.call(div1)先通过数组找到 slice 方法,通过 call 让这个 slice 执行,并且把其中的 this 变成类数组 - 创建一个新数组,把新数组中的内容循环放在新的数组中
- Array.from(xxx)返回一个数组项目跟 xxx 一样
- Object.keys(obj)用来循环对象,返回值是将对象属性名放在一个数组中
- 捕捉报错的方法
try{要执行可能会报错的代码}catch(形参){catch小括号中的形参对应的是上面运行错误信息;上面运行出错,就会走到这个catch,但是不会影响主体代码执行}执行 try 中的代码如果报错就执行 catch 大括号中的代码 - this 指向问题
- 全局下的 this 是 window
- 自执行函数 this 是 window
- 事件绑定对应函数中的 this 是绑定的元素
- 其他看点,点前面是谁 this 就是谁,没有点就是 window
- new 执行类的时候,其中 this 是实例
- 类原型上的方法中的 this 是实例
- call 可以改变函数中 this 指向 f.call(111)
- apply bind 都是改变函数中 this 指向的
- 箭头函数中没有 this,他当中使用的 this 是上级作用域的 this
- apply:apply 的功能和 call 一样;区别在于 call 执行的时候给函数传参是散开写的,apply 执行的时候给函数传参是一个集合的方式
- bind 和 call 的用法相同但是使用 bind 函数不会立即执行,而是会返回一个新函数,新函数执行的时候会让 fn 执行并且把 fn 中的 this 换成指定的 this,后面的参数传给 fn 的实参,新函数执行的时候会给新函数传参,会拼接到 bind 传入实参的后面
let res1 = fn.bind(obj,1,2,3,4,5); - call 不能改变 this 的情况:1、箭头函数;2、bind 的返回值
- Object.create 执行会返回一个空对象,这个对象的__protp__是指向 create 的参数
var obj = Object.create(Array);//obj的__proto__指向Array - class 是 ES6 新增的一个用来创造类的方式,创造出来的类不能当做普通函数执行;
class 创造原型使用 eat(){},class 中没法添加值类型
class Person {
//创造一个名为Person的类
constructor(name, age, sex) {
//constructor是class规定的属性
console.log(arguments);
//设置私有属性
this.name = name;
this.age = age;
this.sex = sex;
}
//私有属性height
height = 1;
//建立名为eat的原型,形参food
eat(food){
console.log(`${food}`)
}
//static声明的是静态属性,指的是Person自己的属性
static qqq = 888;
static ttt = 999;
static yyy = 666;
}
let p1 = new Person("小孔", 12, 0);
console.log(Person.qqq);//输出Person内置的静态属性qqq,它的实例是不能使用静态属性
- 函数的三种角色:普通函数、对象、类
- Array.isArray(xxx):判断 xxx 是不是一个数组
- Array.from(xxx):把类数组 xxx 转成一个数组,返回值就是这个数组
- Object.keys(xxx):把 xxx 这个对象的中的所有私有属性名拿出来组成一个数组返回
//将obj私有属性都拼接起来
Object.prototype.qq = 123;
var obj = {
name: "大哥",
age: 13
}
function obje(one) {
var h1 = document.getElementById("h1");
var a = Object.keys(one);
var str = "";
a.forEach(itm => {
str += `${itm}是${one[itm]};`;
});
h1.innerText = str;
}
obje(obj)
- 在页面插入并点击排序
var list = [{
name: "小红",
age: 10
},
{
name: "小红2",
age: 120
},
{
name: "小红3",
age: 140
},
{
name: "小红4",
age: 105
},
{
name: "小红5",
age: 106
}
];
var ul = document.getElementsByTagName("ul")[0];
var but = document.getElementsByTagName("button");
class Proson {
constructor(ul, but) {
this.ul = ul;
this.but = but;
}
butt(array) {
this.one(array);
this.but[0].onclick = () => {
this.one(this.srop(array));
}
this.but[1].onclick = () => {
this.one(this.srop1(array));
}
}
one(array) {
var str = "";
array.forEach(ele => {
str += `<li>姓名是${ele.name};年龄是${ele.age};</li>`
});
this.ul.innerHTML = str;
}
srop(array) {
return array.sort((a, b) => {
return a.age - b.age;
})
}
srop1(array) {
return array.sort((a, b) => {
return b.age - a.age;
})
}
}
var but = new Proson(ul, but);
but.butt(list)