面向对象编程(Object Oriented Programming҅,OOP),是一种编程范式,它将代码分为具有属性和方法的对象。(对象可以按需求被重用或修改)
编程范式
1:命令式编程
常见的命令式编程有:C语言,C++,Java,C#等命令式编程语言里又可以分为两大类:面向过程和面向对象
面向过程:分析出解决问题所需的步骤然后一步一步解决。
面向对象:把构成问题的各个事务分解成各个对象,建立对象是为了描述某个事务在整个解决问题的步骤中的子能够为 。
2:声明式编程
声明式编程里又可以分为两大类:领域专用语言和函数式编程
领域专用语言:Domain Specific Language,简称DSL。DSL里常见语言有:HTML,CSS,SQL等。
函数式编程:简单来讲就是一种编程模型,将计算机的运行看作是数学中函数的运算。与传统命令式编程最经典的不同:在函数编程中,变量只是一个名称,而不是储存单元。
面向对象与原型对象
1:面向对象
面向对象的特征:封装、继承、多态。
类:OPP中,对象就是从类里面产生出来的。对象与类之间的关系可以总为对象是类的一种概括,而对象是类的一种具体实现。
2:原型对象
通过上图可得以下结论:
a:JavaScript中,每个对象都有一个原型对象。可以通过__proto__属性来访问到原型对象。
b:通过__proto__属性一直网上找的话,最终会找到null。
c:构造函数的prototype属性指向一个对象,这个对象是该构造函数实例化出来的对象的原型对象。
d:JavaScript中的根对象是Object.prototype对象。该对象是一个空对象。
JavaScript中的每一个对象都是从Object.prototype对象克隆而来。而它的原型就是null。
基于对象创建新对象
ES5中提供了Object.create();方法,可以用来克隆对象。
例:
const person = {
arms:2,
legs:2,
walk(){
console.log('walking');
}
}
const xiejie = Object.create(person);
console.log(xiejie.arms); // 2
console.log(xiejie.legs); // 2
xiejie.walk(); // walking
console.log(xiejie.__proto__ === person); // true
原型相关方法
1:prototype和__proto__:
prototype是构造函数上面的一个属性,指向一个对象,这个对象是该构造函数实例化出来的对象的原型对象。实例化出来的对象可以通过__proto__来找到自己的原型对象。
例:
const arr = [1,2,3,4,5];
console.log(Array.prototype); // []
console.log(arr.__proto__); // []
console.log(Array.prototype === arr.__proto__); // true
2: Object.getPrototypeOf():
通过 Object.getPrototypeOf()方法来查找原型对象
例:
let arr = [1,2,3,4,5]
console.log(Object.getPrototypeOf(arr)); // []
console.log(arr.__proto__); // []
console.log(Object.getPrototypeOf(arr) === arr.__proto__); // true
3:constructor 属性:
通过constructor 属性我们可以看到一个对象的构造函数是什么
例:
const arr = [1,2,3,4,5];
console.log(arr.constructor); // [Function: Array]
4:instanceof 操作符:
判断一个对象是否是一个构造函数的实例。
例:
const arr = [1,2,3,4,5];
console.log(arr instanceof Array); // true
console.log(arr instanceof Number); // false
5:isPrototypeOf() 方法:
主要用于检测一个对象是否是一个实例对象的原型对象
例:
const arr = [1,2,3,4,5];
console.log(Array.prototype.isPrototypeOf(arr)); // true
console.log(arr.__proto__.isPrototypeOf(arr)); // true
6:hasOwnProperty() 方法:
判断属性是否为自己本身定义的
例:
const person = {
arms: 2,
legs: 2,
}
const xiejie = Object.create(person, {
name: {
value: "xiejie",
writable: false,
enumerable: true
},
age: {
value: 18,
enumerable: false
}
});
console.log(xiejie.hasOwnProperty("name")); // true
console.log(xiejie.hasOwnProperty("age")); // true
console.log(xiejie.hasOwnProperty("arms")); // false
console.log(xiejie.hasOwnProperty("legs")); // false
构造函数
1:构造函数创建对象
JavaScript是能够模拟出面向对象编程风格,使用函数来模拟其他面向对象语言中的类。
用于实例化对象的函数,我们将之称为构造函数。(函数名首字母大写)
例:
const Computer = function(name,price){
this.name = name;
this.price = price;
}
Computer.prototype.showSth = function(){
console.log(`这是一台${this.name}电脑`);
}
2:ES6中类的声明
使用class关键字声明类,然后从类里面实例化对象。
例:
class Computer {
constructor(name, price) {
this.name = name;
this.price = price;
}
showSth() {
console.log(`zzzz`);
}
}
const apple = new Computer("huawei", 12000);
console.log(apple.name); //huawei
console.log(apple.price); // 12000
apple.showSth(); // zzzz
Computer.prototype.showSth();
面向对象的三大特征
1:继承
从Es6开始,提供了extend关键字
class person{
constructor(name,gender){
this.name=name,
this.gender=gender
}
}
class student extends person{
constructor(name,gender,age){
super(name, age);//代表访问父类
this.age=age
}
}
const ch=new student("gnm","男", 20)
console.log(ch);
2:封装
a:方法借用模式
call()和apply()
call():eg
let obj = {
name: "ghj",
age: 18,
say: function () {
console.log(`我的名字是${this.name}`);
}
}
let obj2 = {
name: "zangsan"
}
obj.say.call(obj2);
apply():eg
const Person = function(name,age){
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
const Student = function(name,age,gender,score){
Person.apply(this,[name,age]);
this.gender = gender;
this.score = score;
}
const xiejie = new Student("ᨀ",18,"ካ",100);
console.log(xiejie.name);
console.log(xiejie.age); // 18
console.log(xiejie.gender);
console.log(xiejie.score); // 100
console.log(xiejie.test);
b:对象冒充
eg:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
const Student = function (name, age, gender, score) {
this.temp = Person;
this.temp(name, age);
delete this.temp;
this.gender = gender;
this.score = score;
}
const xiejie = new Student("ghj", 18, "男", 100);
console.log(xiejie.name);
console.log(xiejie.age); // 18
console.log(xiejie.gender);
console.log(xiejie.score); // 100
3:多态
js天生就是多态,因为是动态语言,不存在类型限制
const duck ={
name: "鸭子",
makeSound:function(){
console.log("嘎嘎嘎嘎");
}
}
const chicken ={
name: "鸡",
makeSound:function(){
console.log("咯咯咯咯");
}
}
const animal = function (obj) {
obj.makeSound();
}
animal(duck);
animal(chicken);
this的指向
this的指向是在函数被调用时决定的
1:函数以普通函数的方式被调用,这个时候this指向全局对象(浏览器:window;Node:global)
function test(){
console.log(this)
}
test()
2:函数以对象的方法的形式被调用,这时候this指向当前对象
let obj ={
say:function(){
console.log(this)
}
}
obj.say();
let test =obj.say;
test();//以普通函数的形式被调用
function test(){
function()test2{
console.log(test)
}
test2()
}
test();//看函数以什么形式被调用//全局
3:箭头函数的this指向,在声明的时候就决定了(********),this和外层作用域相同;
4:this指向绑定事件的元素
<body>
<ul id="color-list">
<li>red</li>
<li>yellow</li>
<li>blue</li>
<li>green</li>
<li>black</li>
<li>white</li>
</ul>
<script>
// this 是绑定事件的元素
// target 触发事件的元素和srcelement等价
let colorList = document.getElementById("color-list");
colorList.addEventListener("click", function (event) {
console.log('this:', this);
console.log('target:', event.target);
console.log('srcElement:', event.srcElement);
})
</script>
</body>
改变this的指向
1:在借用的同时call()和apply()也算是间接修改this指向
eg:
const Test = function () {
this.name = "Js";
this.say = function () {
console.log(`这是{this.name}`);
}
}
const test = new Test();
test.say(); // 这是JavaScript
const a = {
name: "PHP"
};
test.say.call(a); // 这是PHP
2:bind方法绑定this的指向
eg:
const a = {
name: "java"
};
const Test = function () {
this.name = "JavaScript";
this.say = function () {
console.log(`这是{this.name}`);
}.bind(a);
}
const test = new Test();
test.say(); // 这是 java