JS原型对象
Posted chuanq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS原型对象相关的知识,希望对你有一定的参考价值。
一,构造函数,原型对象,实例的关系:
1,构造函数实例:封装的函数,如果通过new操作符来调用的,就是构造函数,如果没有通过new操作符来调用的,就是普通函数
var person1 = new Person(‘Mike‘,10); // 此时的person1 称为person的一个实例,而person就是构造函数了
2,函数Person(对象)有个属性prototype(指针)指向原型对象。 Person.prototype(原型对象,实质也是对象),他有个属性constructor(指针) ,又指向 Person函数对象
console.log(Person === Person.prototype.constructor); //true
3,实例对象person1有个属性[prototype](内部属性,chrome和firefix,Safari,中这个属性叫_proto_)指向原型对象。实例对象可以通过这个属性访问原型对象上的属性和方法(Perons.prototype._proto_ == person1._proto_)。
//举例: function Person(name, age) this.name = name; this.age = age; //在原型对象中添加属性或者方法 Person.prototype.sex = ‘男‘; var person1 = new Person(‘Mike‘,10); var person2 = new Person(‘Alice‘,20); //只给person2设置性别 person2.sex = ‘女‘; console.log(person1.sex) // ‘男‘ console.log(person2.sex) //‘女‘ //解释:这里我们没有给person1实例设置sex属性,但是因为[Protoptype]的存在,会访问原型对象中对应的属性;同时我们给person2设置sex属性后输出的是‘女‘,说明只有当实例本身不存在对应的属性或方法时,才会去找原型对象上的对应属性或方法
4,可以通过实例对象(person1)的constructor(person1.constructor)访问构造函数,但是constructor本质上是原型对象的属性。
二,继承:
1,继承的主要思路是利用原型链,而原型链的原理是:让一个引用类型继承另一个引用类型的属性和方法。即原型对象通过constructor指向构造函数,实例对象通过_ptoto_指向原型对象。
function A() //在A的原型上绑定sayA()方法 A.prototype.sayA = function() console.log("from A") function B() //让B的原型对象指向A的一个实例 B.prototype = new A(); //在B的原型上绑定sayB()方法 B.prototype.sayB = function() console.log("from B") //生成一个B的实例 var a1 = new A(); var b1 = new B(); //b1可以调用sayB和sayA b1.sayB();//‘from B‘ b1.sayA();//‘from A‘
说明:2-1,最后我们调用了b1的sayB方法,可以执行,为什么?
因为b1有[Prototype]属性可以访问B prototype里面的方法;
2-2,我们调用了b1的sayA方法,可以执行,为什么?
因为b1沿着[Prototype]属性可以访问B prototype,B prototype继续沿着[Prototype]属性访问A prototype,最终在A.prototype上找到了sayA()方法,所以可以执行
3,javascript语言的传统方法是通过构造函数,定义并生成新对象,prototype 属性使您有能力向对象添加属性和方法。下面是通过传统的方式创建和使用对象的案例:
//Person.js文件
function Person(x,y) this.x = x; this.y = y;
Person.prototype.name = ‘sky‘; //原型对象上的属性
Person.prototype.toString = function () //原型对象上的方法
return (this.x + "的年龄是" +this.y+"岁");
export Person;
//index.js文件
import Person from ‘./Person‘;
let person = new Person(‘张三‘,12);
console.log(person.toString());
4, 代码中,对象p的的name属性为私有变量,使用p.name不能直接访问
5,以上代码ES6 类class写法:
//Person.js class Person // 构造 constructor(x,y) //构造方法 this.x = x; this.y = y; //方法之间不需要逗号分隔,加了会报错 toString() //原型对象上的方法 return (this.x + "的年龄是" +this.y+"岁"); export Person; //index.js import Person from ‘./Person‘; let person = new Person(‘张三‘,12); console.log(person.toString());
constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个默认的constructor方法会被添加。所以即使你没有添加构造函数,也是有默认的构造函数的。一般constructor方法默认返回实例对象this。类的方法内部如果含有this,它默认指向类的实例。。(个人理解:
Person.prototype = this = person1
Person.prototype.constructor = Person,
person1.constructor == Person
person1.__proto__ === person1.constructor.prototype)
6,下面 这个类的名字是Expression而不是Expre,Expre只在Class的内部代码可用,指代当前类。
const Expression = class Expre static getAge() return ‘12‘; getClassName() return " ClassName1= " +Expre.name + " ClassName2= " +Expression.name; ; let exp = new Expression(); console.log(exp.getClassName());//ClassName1= Expre ClassName2= Expre console.log(Expression.getAge());
说明:Expre.name和Expression.name返回的都是Expre,返回的都是当前类名
如果类的内部没用到的话,可以省略Expre,也就是可以写成下面的形式:
const MyExpre = class getClassName() return MyExpre.name; let myExpre = new MyExpre(); console.log(myExpre.getClassName());//MyExpre
6,立即执行class:
let person = new class // 构造 constructor(props) this.props = props; getProps() return this.props; (‘构造函数的参数‘);
console.log(person.getProps());
//构造函数的参数类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
7, 二次说明:
var o1 = ;
var o2 =new Object();
var o3 = new f1();
function f1();
var f2 = function();
var f3 = new Function(‘str‘,‘console.log(str)‘);
console.log(typeof Object); //function
console.log(typeof Function); //function
console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function
console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object
每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性
原型对象(Person.prototype)是 构造函数(Person)的一个实例
构造器
var obj =
它等同于下面这样:
var obj = new Object()
obj 是构造函数(Object)的一个实例。所以:
obj.constructor === Object
obj.__proto__ === Object.prototype
同理:
var b = new Array(); b.constructor === Array; b.__proto__ === Array.prototype; var c = new Date(); c.constructor === Date; c.__proto__ === Date.prototype; var d = new Function(); d.constructor === Function; d.__proto__ === Function.prototype;
试题:
1,因为 Person的构造函数 === Function
所以 Person.__proto__ === Function.prototype
2,Person.prototype 是一个普通对象,我们无需关注它有哪些属性,因为一个普通对象的构造函数 === Object
所以 Person.prototype.__proto__ === Object.prototype
2,Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。
Object.prototype.__proto__ === null
Function.prototype.__proto__ === Object.prototype
所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)
所有对象的 __proto__ 都指向其构造器的 prototype
Boolean.__proto__ === Function.prototype // true Boolean.constructor == Function //true String.__proto__ === Function.prototype // true String.constructor == Function //true // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Object.__proto__ === Function.prototype // true Object.constructor == Function // true // 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身 Function.__proto__ === Function.prototype // true Function.constructor == Function //true Array.__proto__ === Function.prototype // true Array.constructor == Function //true RegExp.__proto__ === Function.prototype // true RegExp.constructor == Function //true Error.__proto__ === Function.prototype // true Error.constructor == Function //true Date.__proto__ === Function.prototype // true Date.constructor == Function //true
Number.__proto__ === Function.prototype // true Number.constructor == Function //true
Math.__proto__ === Object.prototype // true
Math.construrctor == Object // true
JSON.__proto__ === Object.prototype // true
JSON.construrctor == Object //true
// 上面的函数对象自定义的,
函数声明 function Person() // 函数表达式 var Perosn = function() console.log(Person.__proto__ === Function.prototype) // true
Array.prototype 继承了对象的所有方法,当你用Array.hasOwnPrototype()时,JS 会先查一下它的构造函数 (Array) 的原型对象 Array.prototype 有没有有hasOwnPrototype()方法,没查到的话继续查一下 Array.prototype 的原型对象 Array.prototype.__proto__ === Object.prototype有没有这个方法
function Person(name) this.name = name // 修改原型 Person.prototype.getName = function() var p = new Person(‘jack‘) console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // true
-----------------------------------------
function Person(name) this.name = name // 重写原型 Person.prototype = getName: function() var p = new Person(‘jack‘) console.log(p.__proto__ === Person.prototype) // true console.log(p.__proto__ === p.constructor.prototype) // false
这也很好理解,给Person.prototype赋值的是一个对象直接量getName: function(),使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object,Object.prototype是一个空对象,自然与getName: function()不等。
1,对于私有属性,我们是不可以直接通过 Class 实例来引用的,这也是私有属性的本来含义。但是有一种情况除外,在 Class 定义中,我们可以引用 Class 实例的私有属性:
class Foo #privateValue = 42; static getPrivateValue(foo) return foo.#privateValue; Foo.getPrivateValue(new Foo()); // >> 42
1,数据描述,Object.defineProperty(obj, prop, descriptor) <在ie8下只能在DOM对象上使用>。
obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性-->
configurable: false, //是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)
enumerable:false, //是否可以被枚举(使用for...in或Object.keys())
value:任意类型的值, //prop key对应的值
writable:false //是否可以被重写
以上默认值全为false
var obj = Object.defineProperty(obj,"newKey", value:"hello", writable:false, enumerable:false, configurable:true ); //删除属性 delete obj.newKey; console.log( obj.newKey ); //undefined
2.存取器描述(注意:当使用了getter或setter方法,不允许使用writable和value这两个属性)
当使用存取器描述属性的特性的时候,允许设置以下特性属性:
var obj = ; Object.defineProperty(obj,"newKey", get:function () | undefined, set:function (value) | undefined configurable: true | false enumerable: true | false ); var obj = ; var initValue = ‘hello‘; Object.defineProperty(obj,"newKey", get:function () //当获取值的时候触发的函数 return initValue; , set:function (value) //当设置值的时候触发的函数,设置的新值通过参数value拿到 initValue = value; ); //获取值 console.log( obj.newKey ); //hello //设置值 obj.newKey = ‘change value‘; console.log( obj.newKey ); //change value
3, 使用立即执行函数来实现模块化,所谓模块化,就是根据需要控制模块内属性与方法的可访问性,即私有或者公开。在代码中,module为一个独立的模块,N为其私有属性,print为其私有方法,return 里的decription为其公有属性,add为其共有方法。
4,变量提升,变量声明被提升了,而变量赋值不会被提升。即被定义了但值为undefined
5,柯里化,即Currying,可以是函数变得更加灵活。我们可以一次性传入多个参数调用它;也可以只传入一部分参数来调用它,让它返回一个函数去处理剩下的参数。
var currying = function(x) return function(y) return x + y; console.log(currying(1)) //ƒ (y)eturn x + y; console.log(currying(1)(1)) //2 var cc = currying(1) console.log(cc(1)) //2 console.log(cc(11)) //12
6,apply方法使用数组指定参数,而call方法每个参数单独需要指定:
- apply(thisArg, [argsArray])
- call(thisArg, arg1, arg2, …)
- this.Arg表示指向的对象,arg1表示_proto_的方法传的形参
var user = name : ‘qinhuansky‘, say : function(yaya) console.log(this.name +‘-‘+ yaya) user.say() //qinhuansky-undefined var user2 = name: "qin" user.say.call(user2, ‘haha‘) //qin-haha
使用bind方法,可以为函数绑定this值,然后作为一个新的函数返回:
tip:
1,常量大小字母;注释只有在写业务逻辑判断才写
2,词法环境:通过function声明的函数和var 定义的变量,都会有个window预处理阶段,提前调用为,对一个函数的引用和undefuned
参考简书:https://www.jianshu.com/p/dee9f8b14771
以上是关于JS原型对象的主要内容,如果未能解决你的问题,请参考以下文章