原型和原型链
参考网站
第一,一切都是对象吗?1.1数据类型1.2基准类型和参考类型差异1.3原始值是对象吗?1.4包装器对象1.5摘要2、构造函数2.1构造函数和常规函数的区别2.2 new关键字2.3内置构造函数(构造函数)2.4常规对象和函数对象3、原型和原型链3.1 instanceof3.2 constructor属性3.3原型对象(原型显示)3.4 _ _
什么是对象?对象是JavaScript基本数据类型。对象是复合值。也就是说,收集可通过名称访问的大量值(原始值或其他对象)。
在JavaScript中,对象是具有属性和方法的数据
JavaScript对象可以动态添加和删除属性。
1.1数据类型
条件类型(原始类型):未定义、null、布尔、编号、字符串
参照类型(对象类型):object、array、Math、regexp、function、特殊基本程序包类型(String、Number、boolean)和单体内置对象(grant
1.2基准类型和参照类型之间的差异
基准类型值是不变的,参照类型值是变量//exam
Var str='You can you up '
()
Con(str)//exam
Var str='You can you up '
='LW '
=function(){}
Con()
Con()//exam
var person={ };
='杰夫'
=18;
=function(){ con();}
();
Delete
();基本类型的比较是值的比较,参考类型的比较是参考的比较var a=1。
Var b=true
Con(a==b)
Var a=' jozo
Var b=' jozo
Con(a===b)# exam
var person 1={ };
var person 2={ };
复制Con(person1==person2)变量时的原始值:如果将存储原始值的一个变量复制到另一个变量,则会将原始值的副本分配给新变量,之后两个变量完全独立,只有相同的value。
参考值:如果将存储对象内存地址的变量复制到另一个变量,则此内存地址将分配给新变量。
也就是说,两个变量都指向堆内存中的同一个对象,对其中一个的更改将反映在其他项目中。
# exam
Var a=1
Var b=a
B=2
Con(a)
Varobj1={name:' Jeff 'age:18}
Var obj2=obj1
Obj2.age=20
Con(`obj1=${obj1} `)参数传递的其它原始值:只将变量的值传递给参数,后续参数和此参数不会互相影响。
引用值:对象变量中的值是堆内存中此对象的内存地址
# exam
Var a=1
Var b={name:'jeff'}
Function set(obj){
Obj.age=20
}
三(a)
三(b)
Con(a)
Con(b)1.3原始值是对象吗?
Var a=`
产品:“这个需求什么时候结束?”
程序猿:“下班前结束吧。”
第二天.
产品:“你昨天说下班需求都做完了,现在不也做完了吗!”而且,
程序猿:“我还没有下班。”
```
Con) //89其中a只是字符串,不能有属性和方法
,但事实上他有自己的属性和方法,为什么?1.4 包装对象
其实在上面的例子中在读取字符串的时候会创建一个对象,但是这个对象只是临时的,所以我们称它为临时对象,学术名字叫包装对象。说它临时,是因为我们在读取它的属性的时候,js 会把这个 string 字符串通过 new String()方式创建一个字符串对象,有了对象自然就有了属性,但是这个对象只是临时的,一旦引用结束,这个对象就被销毁了。
var str = "You can you up"
con)
// 类似于下面的代码
con(new String(str).length)
同理数字、布尔值在读取属性的时候也可以通过自己的构造函数来创建自己的一个临时对象
思考
typeof null
typeof undefined
1.5 1 总结
JavaScript 除 undefined 外 几乎所有事物都是对象或看成对象(number,string,boolean)
二、构造函数
Javascript 中的函数即可以当普通函数调用,也可以是构造函数,普通函数通过 new 调用时, 它就是一个构造函数,即构造函数就是一个普通函数。
2.1 构造函数与普通函数的区别
- 在命名规则上,构造函数一般是首字母大写,普通函数一般是驼峰命名法;
- 调用时,普通函数是直接调用,构造函数通过 new 关键字来生成新的实例(对象)(函数对象);
- 构造函数使用 this 定义成员变量和成员方法,普通函数中不使用 this 关键字定义成员变量和方法。
- 返回值,普通函数可以有 return, 构造函数一般没有返回值;
- 构造函数没返回值时,返回实例对象,有返回值时,校验返回值类型,当为非引用类型时,返回的还是实例对象,当为引用类型时,返回该引用类型
// exam
function Person(name, age) {
= name
= age
= function(){
con(,`..`)
}
}
let person1 = new Person('lw', 30)
()
// exam
function Person(name, age) {
= name
= age
= function () {
con()
}
return 'hello'
return {
a: '1',
b: '2'
}
}
let person1 = new Person('lw', 30)
con(person1)
2.2 new 关键字
new 执行过程
let person = new Person('lw',30)
//伪代码
var obj = {}
obj.__proto__ = Per
Per(obj)
return obj
- 创建空对象
- 将空对象的 __proto__ 指向构造函数的 prototype 对象(在此建立 原型链 obj->Per->Object.prototype->null)
- 改变作用域,即将 Person 函数 this 指向新对象 obj,使 obj 继承 Person 的属性和方法,并调用 Person 函数('lw',30))
- 判断返回值,返回新对象
2.3 内置构造函数(构造器)
Javascript 内置构造函数有很多,主要是基本类型、引用类型(复合类型)
Object
var obj = new Object()
Function
var sum = Function("a","b","con(a+b)")
sum(1,2)
Array
var arr = new Array()
RegExp
var reg = new RegExp("\\d","g")
Number、 String、 Boolen
var num = new Number(20)
var str = new String("test")
var bool = new Boolean(false)
2.4 普通对象与函数对象
凡是通过 new Function()创建的对象都是函数对象,其它的都是普通对象。
// exam
var o1 = {};
var o2 = new Object();
var o3 = new f1();
function f1() {};
var f2 = function () {};
var f3 = new Function('str', 'con(str)');
con(typeof Object);
con(typeof Function);
con(typeof f1);
con(typeof f2);
con(typeof f3);
con(typeof o1);
con(typeof o2);
con(typeof o3);
上例中,f1,f2,f3 都是函数对象,o1,o2,03 都是普通对象, Object,Function 也是函数对象.
函数对象可以创建普通对象,普通对象无法创建函数对象。
function foo(){}
typeof foo
var f1 = new foo()
typeof f1
三、原型和原型链
3.1 instanceof
instanceof 运算符用来判断一个构造函数的 prototype 属性所指向的对象是否存在另外一个要检测对象的原型链上
var obj = {}
obj instanceof Object // 检测Object.prototype是否存在于参数obj的原型链上。
// exam
Function instanceof Object
Function instanceof Function
Object instanceof Function
Object instanceof Object
Func
Object.prototype
Object 继承自己, Function 继承自己,Object 和 Function 互相继承对方。
// exam
function Person() {}
var person1 = new Person()
con(person1 instanceof Person)
con(person1 instanceof Object)
3.2 constructor 属性
con == Person)
每个实例对象都有 constructor 属性,且 constructor 属性指向构造函数本身。
3.3 原型对象(显示原型)
3.3.1 什么是原型对象?
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个 prototype 属性,这个属性是一个指针,他指向一个对象,这个对象就是原形对象。
每个函数对象都有 prototype 属性
原型对象,就是一个普通对象。原型对象就是 Per。
function Person() {
= 'lw'
= 30
= function () {
con()
}
}
// 改写
function Person(){
}
Per.name = 'lw'
Per.age = 30
Per.sayName = function(){
con()
}
// 再次改写
Per = {
name: 'lw',
age: 30,
sayName: function () {
con()
}
}
在 默认情况下,所有的原型对象都会自动获得一个 constructorn 属性,这个属性是一个指针,指向 prototype 所在的构造函数
Per.constructor == Person
前面提到过
=Person
person1 有 constructor 属性,是因为 person1 是 Person 的实例。
那 Per 为什么有 constructor 属性?同理, Per (你把它想象成 A) 也是 Person 的实例。
也就是在 Person 创建的时候,创建了一个它的实例对象并赋值给它的 prototype,基本过程如下:
var A = new Person();
Per = A;
原型对象(Per)是构造函数(Person)的一个实例
但 Func 除外,它是函数对象,但它很特殊,他没有 prototype 属性(前面说道函数对象都有 prototype 属性)
function Person(){};
con(Per)
con(typeof Per)
con(typeof Func)
con(typeof Object.prototype)
con(typeof Func.prototype)
Func 为什么是函数对象呢?
var A = new Function ();
Func = A;
上文提到凡是通过 new Function( ) 产生的对象都是函数对象。因为 A 是函数对象,所以 Func 是函数对象。
3.3.2 原型对象是用来做什么的呢?
主要用于继承
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
// exam
function Person() {}
Per.name = 'lw';
var person = new Person();
= 'zk';
con()
delete ;
con()
con())
con)
可以让所有的实例对象共享它所包含的属性和方法,减少内存占用
// exam
function A() {
= 'lw'
= function () {
return
}
}
function B() {
= 'zk'
}
B. = function () {
return
}
var a1 = new A()
var a2 = new A()
var b2 = new B()
var b2 = new B()
con(a1)
con(a2)
con(b1)
con(b2)
我们通过使用构造函数 A 创建了两个对象,分别是 a1 , a2 ;通过构造函数 B 创建了两个对象 b1 , b2 ;我们可以发现 b1 , b2 这两个对象的那个 sayHello 方法都是指向了它们的构造函数的 prototype 属性的 sayHello 方法.而 a1 , a2 都是在自己内部定义了这个方法.
定义在构造函数内部的方法,会在它的每一个实例上都克隆这个方法;定义在构造函数的 prototype 属性上的方法会让它的所有示例都共享这个方法,但是不会在每个实例的内部重新定义这个方法 .如果我们的应用需要创建很多新的对象,并且这些对象还有许多的方法,为了节省内存,我们建议把这些方法都定义在构造函数的 prototype 属性上 当然,在某些情况下,我们需要将某些方法定义在构造函数中,这种情况一般是因为我们需要访问构造函数内部的私有变量
总结
每个构造函数都有一个原型对象,原型对象都包含一个指针,指向构造函数,而实例对象都包含一个指向原型对象的内部指针。
function Person(){}
Per.toString = function (){}
var person1 = new Person()
Per.constructor = Person
= Person
3.4 __proto__(隐式原型)
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__的内置属性(隐式原形),它指向构造该对象的构造函数原型。
对象 person1 有一个 __proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Per ,所以:
== Per
3.5 函数对象
所有函数对象的 __proto__ 都指向 Func,它是一个空函数
// exam
function Person() {}
con === Func) //true
con === Func) // true
con === Func) // true
// 所有的构造器都来自于Func,甚至包括根构造器Object及Function自身
Object.__proto__ === Func // true
Object.constructor == Function // true
Func === Func // true
Func == Function //true
所有的构造器都来自于 Func,甚至包括根构造器 Object 及 Function 自身。所有构造器都继承了·Func·的属性及方法。如 length、call、apply、bind
Func.__proto__ === Object.prototype
所有的构造器也都是一个普通 JS 对象,可以给构造器添加/删除属性等。同时它也继承了 Object.prototype 上的所有方法:toString、valueOf、hasOwnProperty 等
Object. === null
3.6 继承
当我们创建一个函数时:
var Person = new Object()
con(Per)
Person 是 Object 的实例,所以 Person 继承了 Object 的原型对象 Object.prototype 上所有的方法。
当我们定义一个数组时:
var arr = new Array()
con)
继承了 Array 的原型对象 Array.prototype 上所有的方法.
此处输出是空数组,可以使用 Object.getOwnPropertyNames 获取所有(包括不可枚举的属性)的属性名不包括 prototy 中的属性,返回一个数组:
var arrayAllKeys = Array.prototype; // [] 空数组
// 只得到 arrayAllKeys 这个对象里所有的属性名(不会去找 arrayAllKeys.prototype 中的属性)
con(arrayAllKeys));
/* 输出:
["length", "constructor", "toString", "toLocaleString", "join", "pop", "push",
"concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach",
"some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight",
"entries", "keys", "copyWithin", "find", "findIndex", "fill"]
*/
细心的你肯定发现了 Object.getOwnPropertyNames(arrayAllKeys) 输出的数组里并没有 constructor/hasOwnPrototype 等对象的方法(你肯定没发现)。
但是随便定义的数组也能用这些方法
var num = [1];
con())
因为 Array.prototype 虽然没这些方法,但是它有原型对象(__proto__)
Array. == Object.prototype
所以 Array.prototype 继承了对象的所有方法,当你用 num.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有 hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.有没有这个方法
3.7 原型链
function Person(){}
var person1 = new Person();
con(person1.**proto** === Per); // true
con(Per.**proto** === Object.prototype) //true
con.**proto**) //null
Person.**proto** == Func; //true
con(Func)// function(){} (空函数)
var num = new Array()
con(num.**proto** == Array.prototype) // true
con( Array.prototype.**proto** == Object.prototype) // true
con) // [](空数组)
con.**proto**) //null
con(Array.**proto** == Func)// true
3.7 总结
原型和原型链是 js 实现继承的一种模式
原型链的形成是靠__proto__,而非 prototype