1.面向过程
是一种解决问题的思维方式,将解决问题的关注点,放在解决问题的每一个详细的步骤上!
2.面向对象(在JavaScript中,所谓的对象,就是键值对的集合。)(编程思想)
是一种解决问题的思维方式,将解决问题的关注点,放在解决问题所需要的一些列的对象身上!
01. 什么是对象?
//万物皆对象
//能够具体到某一项事物上,才叫对象
//例如: 人指的一类事物,就不能称作为对象
02. JS中什么是对象?
//无序的键值对儿集合
3. 面向对象和面向过程的关系
面向对象是对面向过程的封装!
//1. 获取素有的p
var ps = document.getElementsByTagName("p");
//2. 遍历所有的p
for (var i = 0; i < ps.length; i ++){
//3. 给每个p设置样式
ps[i].style.border = "1px solid hotpink";
}
//面向过程的方式存在的问题
//1. 代码重复 冗余
//2. dom的api过于复杂,难以记忆
function tag(tagName){
return document.getElementsByTagName(tagName);
}
function setBorder(eles){
for(var i = 0; i < eles.length; i ++){
eles[i].style.border = "1px solid hotpink";
}
}
setBorder(tag("divs"));
setBorder(tag("ps"));
//上面函数封装存在的问题(函数封装):
//1. 全局变量污染(全局的函数,也算做是全局变量)
//2. 代码结构混乱,不利于维护
//面对对象编程举例(对象封装)
var iQuery = {
selector: {
tag: function (tagName){
return document.getElementsByTagName(tagName);
},
id: function(){
},
class: function(){
}
},
style: {
setBorder: function (eles){
for(var i = 0; i < eles.length; i ++){
eles[i].style.border = "1px solid hotpink";
}
},
css: function(){
},
addClass: function(){
}
},
ajax: {
get: function () {},
post: function () {}
},
animation: {
animate: function(){},
slideDown: function(){}
}
}
var divs = iQuery.tag("div");
iQuery.setBorder(divs);
优点: // 有利于代码复用!
4.面向对象的三大特性
01.封装性
将功能的具体实现封装在对象的内部,只对外界暴露指定的接口,外界在使用的时候,只需要关注接口如何使用,而不需要关心内部的具体实现!
02.继承性
一个对象没有一些属性和方法,另一个对象有,拿过来使用,就是继承
在JavaScript中,继承就是当前对象可以使用其他对象的方法和属性。
03.多态性
js中没有多态
前提:1必须有继承关系,
2父类的变量指向了子类的对象!
多态在强类型语言中,必须要有继承关系,还要有父类的变量指向子类的对象
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
例:
动物 animal = new 子类(); // 子类:麻雀、狗、猫、猪、狐狸...
动物 animal = new 狗();
animal.叫();
5.创建对象的方式
01.字面量
//优点:简洁,容易写!(只用一次的对象,例如$.ajax({},css({}))
//缺点:复用性差
var obj = {
name: "",
age: 18
};
02.内置构造函数对象
var obj = new Object();
obj.name = "";
obj.age = "";
//缺点:复用性差
03.自定义构造函数对象
function Person (name, age) {
this.name = name;
this.age = age;
}
var p = new Person("张惠妹", 18);
var p1 = new Person("张学友", 18);
优点: 复用性比较好
6.构造函数
01.什么是构造函数
构造函数就是一个函数,这个函数用来初始化对象!
02.构造函数的特征
function Person(){
this.name = "张学友";
this.age = 18;
this.sing = function () {
console.log("我和你吻别,在无人的街");
}
// return new Date();
// return 1;
}
var p = new Person();
// var p = Person();
001.函数名首字母大写(不是必须的)
002.要和new关键字一起使用
003.this指向的是new创建出来的对象
004.不需要写return语句,默认返回new创建出来的对象
03.构造函数的执行过程
001.先使用new关键字开辟空间,创建对象
002.调用构造函数,将构造函数中的this指向new创建出来的对象
003.执行构造函数中的代码,使用this为创建出来的对象添加属性和方法(初始化)
004.默认将new创建的对象返回;
04.构造函数的注意事项
001.构造函数有自己默认的返回值,不需要写return语句
A. 如果写了return语句
a.如果return的是值类型的数据, 对默认返回值不会产生任何影响
b.如果return的是引用类型的数据,将会返回这个return语句返回的引用类型的数据,而不是默认的返回值(new创建出来的对象)
002.如果把构造函数当做普通函数调用
A.返回值就按照正常的函数分析,有return语句,那就返回return的内容,没有return语句,那么就默认返回undefined
B.this会指向window对象,给this加的所有的属性,全都加给了window对象,也就相当于是作为了全局变量!
7.构造函数存在的问题及解决方案
01.问题
给对象的方法赋值的时候,将函数的声明写在构造函数当中,那么每创建一个对象,构造函数就会被调用一次,构造函数中的函数声明也会被调用一次,就会早成,有多少个对象,就会有多少个函数的副本,其实每个对象要用的函数都是一样的,这样,就造成了资源浪费!!
02.解决方案
将函数声明,从构造函数中提取出来,放在全局,那么这个声明就只会被执行一次,在构造函数中要为对象的属性赋值的时候,只需要将已经声明好的函数的地址赋值给对象的方法,即可,这样,所有的对象就都共享全局的函数,也就避免了资源浪费的问题!
03.造成的问题
001. 全局变量
002. 代码结构混乱
8.原型
01.原型是什么
构造函数在创建出来是后,系统会自动的帮这个构造函数创建并且关联一个空对象,这个空对象就是原型
02.原型的作用
原型是一个对象,这个对象中的所有方法和属性都可以被和他关联的构造函数所创建出来的所有的对象共享!
03.原型如何访问(访问方式)
001.构造函数.prototype
Person.prototype.sayHello = function () {
console.log("我是一个放在原型中的方法");
}
var p = new Person();
p.sayHello();
var p1 = new Person();
p1.sayHello();
console.log(p.sayHello === p1.sayHello);//true
console.log(p1.sayHello === Person.prototype.sayHello);//true
002.创建出的对象.__proto__(非标准属性,有兼容性问题,不推荐使用)
9.原型的使用注意事项
001. 一般只将需要共享的内容放在原型中(大部分时候都是方法)
002.当使用对象访问某个属性的时候,会遵守属性搜索原则(当使用对象访问某一个属性的时候,会先在对象自身进行查找,如果找到了就直接使用,如果没有找,就会去原型中进行查找)
003.当使用对象为某个属性赋值的时候,不会遵守属性搜索原则,只会在对象自身进行查找,如果找到了就修改,找不到就添加
004.当为构造函数的prototype属性赋值的时候,赋值之前创建的对象和赋值之后创建的对象的原型不一致!
function Person () {
}
Person.prototype.sayHello = function () {
console.log("Nice to meet you");
}
var p = new Person();
p.sayHello();//Nice to meet you
Person.prototype = {
sayHi: function () {
console.log("Nice to meet you too");
}
}
var p1 = new Person();
p1.sayHi();//Nice to meet you too
p1.sayHello();//undefined
p.sayHello();//Nice to meet you
p.sayHi();//undefined
10.原型的使用方式
001.利用对象的动态特性为原型新增属性和方法(新增的内容比较少的时候使用)
Person.prototype.sayHi = function () {
}
Person.prototype["sayHello"] = function () {
}
002.直接为构造函的prototype属性赋值(新增的内容比较多的时候使用)
Person.prototype = {
sayHi: function () {
},
sayHello: function () { },
name: "张学友"
};
11.constructor属性
001原型对象中,默认会有一个constructor属性
002.这个属性指向的就是和这个原型关联的构造函数
function Person () {
}
// console.log(Person.prototype.constructor === Person);//true
003.当手动给构造函数的prototype属性赋值一个新对象之后,这个新对象是不会自动加上constructor属性的
004.一般情况下,在给prototype属性赋值的时候,都会在对象中自己手动的添加一个constructor属性
Person.prototype = {
constructor: Person
};
var p = new Person();
console.log(p.constructor);
12.原型链
001.概念:
对象都有原型,原型也是对象,所以原型也有原型,这样子就会形成一个原型组成的链式结构,称作原型链
//p----->Person.prototype----->Object.prototype------>null
//arr -----> Array.prototype----->Object.prototype -----> null
//date -----> Date.prototype -----> Object.prototype ---- > null
//obj -----> Object.prototype ---- > null
002.Object.prototype是js中所有对象的原型链的终点!
可以说Object.prototype 是js中所有对象的 祖宗!!
003.属性搜索原则:
//当通过一个对象访问某个属性的时候,遵守如下原则:
//1. 在对象自身进行查找,如果有这个属性,就直接使用
//2. 如果没有找到,那么就去对象的原型中进行查找,如果有这个属性,就直接使用
//3. 如果没有找到,那么就继续去原型的原型中进行查找,如果有这个属性,就直接使用
//4. 如果没有找到,就继续沿着原型链向上查找,直到找到为止,或者找到Object.protottype为止
完整原型链
1. 先画对象的原型链
2. 再画函数的原型链
3. 把Object构造函数画进来,找到和图中内容的关系
4. 把Function当做对象处理
function Person () {
}
var p = new Person();