1、面向对象(OOP | OO)
- 对象:黑盒子{ Math(abs random ceil floor)、Date、Object(对象,没有功能,是所有js对象的父级)}
盖楼房:调用具有某些功能的人来实现某些功能。
对象是一个整体,对外提供一些功能和属性。使用对象时只关注对象的提供的功能,不关注对象的内部实现。 - 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可 以设计出低耦合的系统,使系统 更加灵活、更加易于维护
- 缺点:性能比面向过程低
2、面向过程
- 过程:过程就是面向函数式编程(function)。
- 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源; 比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
- 缺点:没有面向对象易维护、易复用、易扩展
3、面向对象的特点:(封装,继承,抽象(多态))
- 抽象:就是建模,抓住核心问题。
- 封装:
- 使用者:不必考虑内部实现,只考虑内部提供的功能。
- 创建者:考虑好对外提供的功能,实现内部的代码。
- 继承:
- 从已有的对象上继承出新的对象,新对象具有了老对象的一些功能和特性。
- 多重继承:
沙发 汽车(带轮子的沙发)
盒子 汽车(集装箱货车) - 多态:龙生九子
- 属性的继承使用 call(this)
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function A(){ //构造函数
this.name='lisisi';
this.age = 18;
}
A.prototype.sss = 'sss';
A.prototype.func = function(){
console.log(this.name);
}
A.prototype.func1 = function(){
console.log(this.age);
}
function B(){
A.call(this); //修改A的this,相当于A里面的属性是给B添加的。
}
// B.prototype = A.prototype; //引用类型
for(let m in A.prototype){
B.prototype[m] = A.prototype[m];
}
let a = new A();
let b = new B();
// B.prototype=A.prototype;
console.log(B.prototype);
console.log(A.prototype);
B.prototype.func1 = function(){
console.log(1);
}
b.func1(); //1
a.func1(); //18
</script>
</body>
</html>
4、对象的组成
- 属性:属于对象的变量
- 方法:属于对象的函数
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//工厂方法创建对象
function MakeStu(name,weight){ //构造函数 参数就是原料
//加工
var obj = new Object();
//属性
obj.name = name;
obj.weight = weight;
//方法
obj.getName = function(){
console.log('我的名字叫:'+this.name);
}
obj.getWeight = function(){
console.log('我的体重是:'+this.weight);
}
//出厂
return obj;
}
var obj1 = MakeStu('limao','100kg');
obj1.getName();
obj1.getWeight();
var obj2 = MakeStu('lihaoshung','90kg');
obj2.getName();
obj2.getWeight();
</script>
</body>
</html>
5、this 函数的调用者就是this(每一个函数都具有自己的调用对象)
- 事件:触发事件的对象
div.onclick = function(){
alert(this);
};
6、new : 一个函数( 创建对象实例)。
- new的深入操作:
- 创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的prototype属性。
- 将这个空对象赋值给函数内部的this关键字。
- 开始执行构造函数内部的代码。
- 将对象实例返回。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function show(){
//this=new Object();
alert(this);
//return this;
}
show(); //window
new show(); //object
</script>
</body>
</html>
- 防止漏掉new造成错误:
- 在构造函数内部使用严格模式。
- 使用instanceof在内部判断。判断是否为当前对象的实例。
- 使用new.target 在内部判断,new.target指向自己的构造函数。
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
//严格模式
function A(name){
'use strict';
this.name = name;
}
let a = new A('ahuang');
console.log(a.name); //ahuang
//递归
function B(name){
//this 指向新创建的实例
if (!(this instanceof B) ) {
return new B(name);
}
this.name = name;
}
let b1 = new B('zhangsan');
console.log(b1.name); //zhangsan
let b2 = B('lisi');
console.log(b2.name); //lisi
function C(name){
if (!(new.target == C)) {
throw new Error('这个对象必须使用new来创建对象');
}
this.name = name;
}
let c1 = new C('ergouzi'); //ergouzi
console.log(c1.name);
let c2 = C('wangwu'); //这个对象必须使用new来创建对象
</script>
</html>
7、构造函数里面的return语句:
- 任何一个函数都可以使用new。返回值都是一个对象。如果这个函数是一个构造函数的话,返回的是这个函数的实例。如果函数是一个普通函数,那么返回的是一个空的对象。
- 如果return的是普通数据类型。那么相当于没写。
- 如果返回的是this,那么返回的与本身返回的是一样的。
- 如果构造函数中具有return语句,并且return返回的是一个对象,那么构造函数产生的对象就是返回的这个对象。而不是构造函数的本身实例。
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
// 简单数据类型
function A(name){
this.name = name;
return 123;
}
let a = new A('12');
console.log(a); //12
//返回this
function B(name){
this.name = name;
return this;
}
let b = new B('34');
console.log(b); //34
//返回其他对象
function C(name){
this.name = name;
return {
"a": a
};
}
let c = new C('56');
console.log(c); //"a":a
</script>
</html>
8、Object对象,是所有JS对象的基础。Object 的原型指向null。一切对象的基础是null,null也叫空。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
console.log(Object.prototype.__proto__); //null
</script>
</body>
</html>
9、Object.create()有的时候我们拿不到对象的构造函数。可以根据这个对象的某一个实例去创建一个对象。
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
function A(name){
this.name = name;
}
let a = new A('lisi');
let b = Object.create(a);
console.log(b); //zhangsan
b.name = 'zhangsan';
console.log(a.name) //lisi
</script>
</html>
10、对于对象来说,每一个属性都有四个描述。
- value 值
- enumerable 枚举、遍历(for in)
- configurable 修改
- writable 删除
后面三个默认值都是true
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
let obj1 = new Object();
obj1.p1 = 'zhangsan';
obj1.p2 = 18;
let obj2 = Object.create(obj1,{
p1:{
value: 'zhangsan', //值
enumerable: false, //遍历 有问题
configurable: true, //修改值
writable: true //删除属性
},
p2:{
value: 18,
enumerable: false,
configurable: true,
writable: true
}
});
for(let i in obj2){
console.log(i,obj2[i]);
}
obj1.p1 = 'lisi';
console.log(obj1.p1); //lisi
delete obj1.p2;
console.log(obj1.p2); //undefined
</script>
</html>
enumerable:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
let obj = {
a: 'a',
b: 'b',
c: 'c',
};
for(let i in obj){
console.log(i,obj[i]); //aa bb cc
}
console.log('-------------');
Object.defineProperty(obj,'c',{
value: 1,
enumerable: false, //遍历
})
for(let i in obj){
console.log(i,obj[i]); //aa bb
}
</script>
</html>
11、JavaScript的面向对象是基于constructor(构造函数)与prototype(原型链)的。
-
构造函数:constructor
- 构造函数就是一个函数。和普通函数有一些区别。
- 函数的内部使用this的关键字。
- 首字母是大写的。
- 使用的时候要用new操作符创建实例对象。
-
原型(prototype):一个函数可以看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的每一个对象都添加一个统一的方法。(统一的给多个对象添加属性或者方法)
- 产生原理:
- JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。
- 通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。
- JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。
- JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。这个对象就是函数的原型。
- 类: 模板(人类)在JS中一般称为对象
- 对象:创建出来的具体的实例(张三)在JS中一般称为对象实例
eg1:
- 产生原理:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
let arr1 = new Array(1,2,3,4,5,6);
let arr2 =new Array(7,8,9);
//原型 给每个对象添加相同的属性
Array.prototype.sum = function(){
let sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
console.log(arr1.sum()); //21
console.log(arr2.sum()); //24
</script>
</html>
eg2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function MakeStu(name,weight){
// var this = new Object();
//属性
this.name = name;
this.weight = weight;
//return this;
}
//方法
MakeStu.prototype.getName = function(){
console.log('我的名字叫:'+this.name);
}
MakeStu.prototype.getWeight = function(){
console.log('我的体重是:'+this.weight);
}
var obj1 = new MakeStu('limao','100kg');
obj1.getName();
obj1.getWeight();
var obj2 =new MakeStu('lihaoshung','90kg');
obj2.getName();
obj2.getWeight();
</script>
</body>
</html>
- 原型也是一个对象(原型对象),里面是所有加在对象原型上的方法以及属性。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
Object.prototype.name = function(){
console.log('a');
};
let obj = new Object(); //a
let obj2 = new Object(); //a
obj.name();
obj2.name();
//给对象再次添加相同的属性,旧的属性会被新的属性覆盖
obj2.name = function(){
console.log('b')
};
obj.name(); //a
obj2.name(); //b
</script>
</body>
</html>
-
原型链:JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。也就是说,所有对象都继承了Object.prototype的属性。这就是所有对象都有valueOf和toString方法的原因,因为这是从Object.prototype继承的。那么,Object.prototype对象有没有它的原型呢?回答是Object.prototype的原型是null。null没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null。
(构造函数创建实例对象,构造函数具有原型,实例对象也具有原型。实例对象的原型指向构造函数的原型。)
图解
- constructor:构造器。默认指向prototype对象所在的构造函数。
-
proto:每一个实例对象都具有的私有属性。指向自己的原型。
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
function A(){
this.a = '1';
}
let b = new A();
console.log(b.__proto__); //object
console.log(A);
</script>
</html>
12、 getPrototypeOf 获取obj对象实例的原型
格式:Object.getPrototypeOf(obj)
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
function A(name){
this.name = name;
}
let a = new A('lisi');
console.log(Object.getPrototypeOf(a)); //object 获取实例对象的原型
console.log(a.__proto__); //object
Object.getPrototypeOf({}) === Object.prototype; //true
// {}:new Object与Object的原型一致 构造函数的原型与对象的原型一致
Object.getPrototypeOf(Object.prototype) === null;
// Object对象的原型的原型指向null,null是原型链的顶点
/*
let o = new Object();
o.__proto__
Object.peototype
Object.peototype.peototype
null
*/
function f(){}
Object.getPrototypeOf(f) === Function.prototype; //true
// 构造函数的原型与内置对象Function的原型一致 f === new Function()
</script>
</html>
13、isPrototypeOf 判断该对象是否为参数对象的原型
格式:Object.prototype.isPrototypeOf()
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
let a = new Array();
console.log(Array.prototype.isPrototypeOf(a)); //true Array.prototype === a.__proto__
</script>
</html>
14、设置一个对象的prototype对象,返回参数对象本身
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
let a = new Array();
console.log(Object.getPrototypeOf(a)); //Array
Object.setPrototypeOf(a,Number.prototype);
console.log(Object.getPrototypeOf(a)); //Number
</script>
</html>
15、getOwnPropertyNames 成员是参数对象本身的所有属性的键名,不包含继承的属性键名。hasOwnProperty 用于判断某个属性定义在对象自身,还是定义在原型链上。
格式:
Object.getOwnPropertyNames()
Object.prototype.hasOwnProperty()
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
function A(name){
this.name = name;
function say(){
console.log('s');
}
}
A.prototype.m = function(){
console.log('m');
};
function B(){
A.call(this);
this.age = 18;
this.sex = 'man';
}
for(let i in A.prototype){
B.prototype[i] = A.prototype[i];
}
let b = new B();
b.m(); //m b继承a的属性
// b.name(); is not a function b本身的属性
//console.log(Object.getOwnPropertyNames(b)); name say age sex
console.log(B.prototype.hasOwnProperty('name')); //false
</script>
</html>
16、私有属性
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
function A(name){
let b = name;
this.getName = function(){
console.log(b); //私有属性
};
}
let a = new A('lili');
// console(a.name) is not a function
a.getName();
</script>
</html>
17、命名空间
eg:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
</body>
<script>
//大型项目组 防止污染
var Li = {
this.F = function(){
}
this.S = function(){
}
}
Li.F();
</script>
</html>
18、json:(字符串)数据传输格式
json 对象:json字符串解析出来的对象,或者就是个对象。但是这个对象有限制。键必须是双引号包起来。值必须是简单类型或者数组。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//json 创建对象的格式
let json = {
"name":"zhangsan",
"age":19,
"getName": function(){
console.log(this.name);
}
};
//单体对象(适用于只有一个对象的场景)
//对象转化为字符串
console.log(JSON.stringify(json));
</script>
</body>
</html>
19、闭包:
函数的作用域是定义的地方,而不是函数调用的地方。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function t(){
var a = 10;
//往外寻找变量,若在函数内部找不到,就去外部寻找(最外层是window),若外部没有找到会产生not defined
return function(){
console.log(++a);
}
}
var a = 1000;
var tmp = t();
tmp(); //11
/*
function t(){
var a = 10;
//return = function(){
console.log(++a);
}; ;
}
var a = 1000;
var tmp = function(){
console.log(++a);
};
tmp(); //1001
*/
</script>
</body>
</html>
20、js的数据类型分为简单类型与复杂类型(引用类型)
- 这两种数据类型保存数据的形式是不同的。
- 简单类型:
数字
字符串
布尔值
undefined
null - 引用类型:
数组
对象 -
图解:
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
let arr = [1,2,3];
let arr1 = arr;
arr1.push(8);
console.log(arr); //1,2,3,8
console.log(arr1); //1,2,3,8
</script>
</body>
</html>
21、instanceof 判断某一个实例对象是否是某一个对象的实例。
- 格式:obj instanceof Object
-
任何实例都是Object的实例
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//数组的实例
let a = new Array();
console.log(a instanceof Number); //false
function Say(name){
this.name = name;
}
let s = new Say();
console.log(s instanceof Say); //true
</script>
</body>
</html>
22、案例
- 选项卡
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
button{
width: 80px;
height: 40px;
margin: 50px;
}
div div{
width: 200px;
height: 200px;
border: 1px solid #ccc;
display: none;
}
.active{
background-color: yellow;
}
</style>
</head>
<body>
<div id="tab">
<button class="active">vf</button>
<button>vdf</button>
<button>vdsv</button>
<div style="display: block;">ddfa</div>
<div>fvd</div>
<div>fva</div>
</div>
</body>
<script>
function Tab(id){
//this = new Tab('tab');
// _this = this;
let obj = this;//保存现场的this this进入一个新的函数会改变,把this赋值给一个不会改变的量
//属性 初始化
this.con = document.getElementById('tab');
this.btn = this.con.getElementsByTagName('button');
this.div = this.con.getElementsByTagName('div');
for (var i = 0; i < this.btn.length; i++) {
this.btn[i].index = i;
this.btn[i].onclick = function(){
//事件内部this发生变化,this变成按钮 this = this.btn[i]
obj.fclick(this);
};
}
}
//方法
//原型:同时给多个对象添加属性
Tab.prototype.fclick = function(btn){
//this 函数的调用者 Tab
for (var j = 0; j < this.btn.length; j++) {
this.btn[j].className = '';
this.div[j].style.display = 'none';
}
//用到按钮的this btn = this = this.btn[i]
btn.className = 'active';
this.div[btn.index].style.display = 'block';
};
new Tab('tab');
// new Tab('abc'); 若使用_this = this(全局变量),再次构造函数会造成函数覆盖,可在构造函数中var/let 变量 = this解决该问题
</script>
</html>
- 数组赋值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
let a = [1,2,3];
let b = [];
for(var j in a){
b.push(j);
}
console.log(a); //123
console.log(b); //123
b.push(8);
console.log(a); //123
console.log(b); //1238
let arr = [1,2,3];
let arr1 = [...arr];
arr1.push(8);
console.log(arr); //123
console.log(arr1); //1238
</script>
</body>
</html>
- 有范围的拖拽封装
function Drag(id){
this.d = document.getElementById(id);
//初始化
this.x = 0;
this.y = 0;
let obj = this;
this.d.onmousedown= function(ev){
var ev = ev || event;
obj.mousedown(ev)
};
}
Drag.prototype.mousedown = function(ev){
//计算鼠标相对div的距离
this.x = ev.clientX - this.d.offsetLeft ;
this.y = ev.clientY - this.d.offsetTop;
let obj = this;
document.onmousemove = function(ev){
var ev = ev || event;
obj.mousemove(ev)
}
document.onmouseup = this.mouseup;
}
Drag.prototype.mousemove = function(ev){
this.d.style.left = ev.clientX - this.x + 'px';
this.d.style.top = ev.clientY - this.y+'px';
};
Drag.prototype.mouseup = function(){
document.onmousemove = null;
document.onmouseup = null;
}
- 有范围的拖拽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div{
width:200px;
height:200px;
background: red;
position: absolute;
}
</style>
</head>
<body>
<div id="move"></div>
</body>
<script src="Drag.js"></script>
<script>
function LimitDrag(id){
Drag.call(this,id); //this LimitDrag()
}
for(let a in Drag.prototype){
//[a]:键名(mousedown mousemove mouseup)
LimitDrag.prototype[a] = Drag.prototype[a];
}
//重写
LimitDrag.prototype.mousemove = function(ev){
//计算div相对浏览器可视窗口的距离
let l = ev.clientX - this.x;
let t = ev.clientY - this.y;
//判断拖拽边界
if(l<0){
l=0
}else if(l>window.innerWidth - this.d.offsetWidth){
l = window.innerWidth - this.d.offsetWidth;
}
if(t<0){
t=0
}else if(t>window.innerHeight-this.d.offsetHeight){
t =window.innerHeight-this.d.offsetHeight;
}
this.d.style.left = l + 'px';
this.d.style.top = t+'px';
};
new LimitDrag('move');
</script>
</html>