1.创建对象(两种)
(1)//普通创建
var person = new Object();
// var person = {}//简写
//函数封装--工厂方式
function createPerson(name) {
//特点:流水线生产
//原材料
var person = new Object();
//加工
person.name = name;
person.showName = function () {
console.log(this.name)
}
//出产品
return person;//返回对象,否则无法创建,为undefine
}
var p1 = createPerson("huahua");//封装方式的创建
p1.showName();打印
(2)//new方法创建即构造函数创建
new关键字作用
/**
1.创建一个新对象
2.将构造函数的作用域赋给新对象(因此,this就指向了这个新对象)
3.指向钩子函数中的代码(为这个新对象添加属性)
4.返回新对象(不用写return)
*/
//改写如下
//构造函数无返回值
function CreatePerson(name){
this.name = name
this.showName = function(){
alert(this.name)
}
}
2.对象引用
//基本类型
var num1 = 20;
var num2 = num1;
console.log(num1)//20
//引用类型
var arr1 = [1, 2, 3];
var arr2 = arr1;
arr2.push(4);
console.log(arr1)//[1,2,3,4]发生了改变
如果从一个变量向另一个变量复制引用类型的值,同样还是会将存储在变量对象中的值复制一份放到新变量分配的空间中,不同的是,复制的这个副本是一个指针,并不是具体的对象,这个指针指向存储在堆内存中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象
3.原型(prototype)的使用
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,这个对象就是构造函数CreatePerson的原型对象,当调用CreatePerson创建出一个具体对象p1后,p1内部也有一个指针指向构造函数的原型对象,同样的道理,不管创建出多少对象,这些对象内部都会有个指针执行构造函数的原型对象,使用原型对象的好处是让所有被创建出来的对象共享这个原型对象所包含的属性和方法
function CreatePerson(name){
this.name = name
}
// 每个函数都有一个prototype对象,即指针(对象的指针指向prototype对象的地址),指向一个对象(原型对象)
//创建出来的对象,也指向原型对象
CreatePerson.prototype.showName = function(){
alert(this.name)
}
var p1 = new CreatePerson("小强")
var p2 = new CreatePerson("小花")
var p3 = new CreatePerson("小豆")
alert(p1.showName === p2.showName) // true
alert(p2.showName === p3.showName) // true
// 错误演示
console.log(p1.prototype)//undeifined,原型是在构造函数上的且不可见的!!!
//浏览器产商 Chrome firefox自己实现了prototype可见,但不是标准!!!
有些浏览器没得
console.log(p1.__proto__);//下划线左右两个,p1.__proto__指向原型对象
面向对象选项卡:
//面向过程版的:
var oWrap = document.getElementById("wrap");
var aBtn = oWrap.getElementsByTagName("button")
var aDiv = oWrap.getElementsByTagName("div")
for(var i=0; i<aBtn.length;i++){
aBtn[i].index = i
aBtn[i].onmouseover = function () {
for(var j=0;j<aBtn.length;j++){
aBtn[j].style.backgroundColor = "white"
aDiv[j].style.display = "none"
}
this.style.backgroundColor = "orangered"
aDiv[this.index].style.display = "block"
}
}
//面向对象版的:
/1./把面向过程的代码整理,功能封装
// 2.面向对象完善
// (1).全局变量就是属性 (2).函数就是方法(3).既不是全局和函数,就封装成初始化方法
//3、调整this指向(出现嵌套代码块,一般this指向不同)
//id用来代表多个选项卡中选中的一个
function Tab(id) {
this.oWrap = document.getElementById(id);
this.aBtn = this.oWrap.getElementsByTagName("button");
this.aDiv = this.oWrap.getElementsByTagName("div");
}
//记得加this,指向
Tab.prototype.changeTab = function (obj) {
for(var j=0;j<this.aBtn.length;j++){
this.aBtn[j].style.backgroundColor = "white"
this.aDiv[j].style.display = "none"
}
obj.style.backgroundColor = "orangered"
this.aDiv[obj.index].style.display = "block"
}
//初始化方法
Tab.prototype.init = function () {
var That = this
for(var i=0; i<this.aBtn.length;i++){
this.aBtn[i].index = i;
console.log("外层",this)
this.aBtn[i].onmouseover = function () {
That.changeTab(this)
console.log("内层",this)
}
}
}
//创建对象,调用
var t1 = new Tab("wrap")
t1.init()
var t2 = new Tab("wrap2")
t2.init()
3.包装对象
字符串为啥可以使用:charAt、indexOf、replace等方法?
-----基本类型都有其对应的一个包装对象,例如:字符串类型对应的包装对象是String,数字的包装对象是Number,布尔类型对应的包装对象是Boolean。根据原型知识,我们可以猜测一下charAt可能是挂载到String构造函数原型上的,所以当str对象去调用charAt方法是可以正常运行的
String.prototype.charAt = function(){}
//给String构造函数自定义一个方法,看普通的字符串是否也能够使用
//创建一个字符串对象
var str1 = new String("hello")
String.prototype.test = function(){
alert(1)
}
//创建一个基本类型字符串
var str2 = "hello2"
str2.test() // 弹出1
//如果给str2挂载一个属性,看看效果
str2.num = 10
alert(str2.num) // undefined
//从上面的代码我们知道了 基本类型的str2可以使用方法,实际上使用的是String身上的方法
上面代码实际上执行了以下几个步骤:
1.创建String 类型的对象
2.在对象上调用指定的方法
3.销毁这个对象
4.原型链:
原型链就是实例对象和原型之间的链接。每个函数都有一个prototype属性,这个prototype指向一个对象(利用包装对象.prototype.方法名 = function(){}创建的方法),这个对象叫做原型,通过构造函数创建出来的实例也有一个内部指针指向原型,这样就形成实例对象和原型直接的链接,我们把这个链接称为原型链。
原型链的层级关系
原型链有多层,最外层为Object.prototype
function Fn1(){
this.num = 10;
}
Fn1.prototype.num = 20
Object.prototype.num = 30
var f1 = new Fn1()
console.log(f1.__proto__)
console.log(f1.__proto__.__proto__.num) //30
-------------------------------------------------------------------------------------------------
//es6 中获取原型,取代__proto__的方法Object.getPrototypeOf(对象实例)
console.log(Object.getPrototypeOf(f1) === f1.__proto__);//true
//注意:prototype 是在构造函数上,__proto__在对象上
//访问属性的优先级---就近原则,例如:f1,Fn.prototype, Object.prototype上都有num属性,
//那么alert(f1.num)的时候,首先取出的是f1身上的值,如果f1身上没有,那么才访问Fn.prototype,
//以此类推,访问到最外层Object.prototype,如果最终都没有就返回undefined
alert(f1.num)
5.面向对象的相关属性和方法
//hasOwnProperty
function Fn() {
this.name = 20;
}
Fn.prototype.age = 40;
var f1 = new Fn();
alert(f1.hasOwnProperty("name"))//true
alert(f1.hasOwnProperty("age"))//false
alert(Fn.hasOwnProperty("name"))//true
//constructor
console.log(f1.constructor)//查看该对象的构造函数
console.log(Fn.prototype.constructor)
function Fn1(){}
// 指向Fn1的
Fn1.prototype.name = 10
Fn1.prototype.age = 20
// 指向Object的,把一个对象赋值给原型对象,被后面的对象覆盖了
Fn1.prototype = {
name: 10,
age: 20
}
// 指向Object
console.log(Fn1.prototype.constructor)
// 当一个构造函数新创建出来后,会自动生成下面一句话
Fn1.prototype.constructor = Fn1
// 上面的写法不经意间就会把constructor修改掉,因此,我们需要手动去修正
function Fn1(){}
Fn1.prototype = {
constructor: Fn1,
name: 10,
age: 20
}
// 指向Fn1
console.log(Fn1.prototype.constructor)
//自己添加的num属性是可以被打印出来的,系统自带的没办法for in循环出来
Fn1.prototype.num = 10
for(var attr in Fn1.prototype){
console.log(attr) //把constructor name age num属性打印了一遍,没打印值
}
//instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性
alert(f1 instanceof Fn1) //true
alert(f1 instanceof Array) //false
//toString()
// toStringtoString的作用是把对象转换成字符串,如果是系统自带的对象去调用toString方法, 那么toString方法在原型对象上,如果不是系统自带的对象去调用toString方法,那么toString在Object.prototype上
var arr =[1,2,3]
// 调用的是Array.prototype原型对象上的toString();
console.log(arr.toString())
console.log(f1)//toSting()在Object上
// 改写应用与分割字符
Array.prototype.toString = function () {
return this.join("-")
}
console.log(arr.toString())
//转换16进制颜色
var num1 = 255;
console.log(num1.toString(16))//打印ff
//判断类型(推荐第三种)
// 判断类型方法 consructor instanceof toString
var arr=[]
console.log(arr.constructor == Array)//true
console.log(arr.prototype)//undefined
console.log(arr.prototype instanceof Array)//false
console.log(arr instanceof Array)//true
console.log(arr.toString() === Array)//false
// //call(对象)改变内部this指向,应用某一对象的一个方法,用另一个对象替换当前对象
console.log(Object.prototype.toString.call(arr))//[object Array]
console.log(Object.prototype.toString.call())//[object Undefined] 因为没有传入
console.log(Object.prototype.toLocaleString.call(arr))//1-2-3
6.call()方法和apply()方法。都是调用一个对象的一个方法,以另一个对象替换当前对象,可以理解为借用一个对象的方法。传参形式不同!
function Fn1(){
}
function Fn2(){
}
Fn1.prototype.showAge = function (age,age2) {
alert(age)
alert(age2)
}
var f1 = new Fn1()
var f2 = new Fn2()
f1.showAge(20);
f1.showAge.apply(f2)//undefind
f1.showAge.call(f2,20,63)//多个分隔开
f1.showAge.apply(f2,[20,23])//可传多个参数,多个用数组
7.继承
function Parent(name, age) {
this.name = name;
this.age = age;
}
Parent.prototype.showName = function () {
alert(this.name)
};
function Child(name, age) {
//Parent里面的this指向会出现问题,直接调用Parent()函数,相当于window.Parent(),那么函数里面的this是指向window的,这个时候,我们需要修正Parent里面的this,让Parent里面的this指向Child才行
//Parent.call(name, age)
//不能具体指明对象,new的对象,很多且不同
Parent.call(this, name, age)
}
//继承方法,把Parent的原型赋值给Child原型
//对象引用,两个对象指向同一个地址,,任何一个对象的修改都会影响另一个对象
// Child.prototype = Parent.prototype;
//影响:父中和子中都有同样的方法。解决如下:
function extend(obj1,obj2){
//循环obj2的内容,赋给obj1。for in专门循环对象
for (var attr in obj2) {
obj1[attr] = obj2[attr]
}
}
//封装extend方法的原因,直接对象赋值给对象的话,会造成对象的引用,两者相互影响
Child.prototype.showAge = function () {
alert(this.age)
};
var c1 = new Child("小红", 222)
c1.showName()
c1.showAge()