一、object的简写
1、属性的缩写
let name = 'zhuangzhuang';
let age = 18;
let obj = {
name:name,
age:age
}
那么对于这种value为变量,key跟value名字相同的,那么可以简写成如下:
let name = 'zhuangzhuang';
let age = 18;
let obj = {
name,
age
}
2、方法的缩写
let obj = {
say:function(){
console.log('nihao');
}
}
那么对于这种函数,可以简写成如下:
let obj = {
say(){
console.log('nihao');
}
}
3、对象转成字符串
let name = 'zhuangzhuang';
let age = 18;
let obj = {
name:name,
age:age,
say:function(){
console.log('nihao');
}
}
console.log(JSON.stringify(obj));//{"name":"zhuangzhuang","age":18}
let name = 'zhuangzhuang';
let age = 18;
let obj = {
name,
age,
say(){
console.log('nihao');
}
}
console.log(JSON.stringify(obj));//{"name":"zhuangzhuang","age":18}
可见,不管是简写还是不简写,对象在转成字符串的时候,都会先将变量解析,然后再转成字符串。
但是,务必要注意一点,对于对象中的函数,JSON.stringify是直接忽略的,如下:
let obj = {
say:function(){
console.log('nihao');
}
}
console.log(JSON.stringify(obj));//{}
let obj = {
say(){
console.log('nihao');
}
}
console.log(JSON.stringify(obj));//{}
二、Object.setPrototypeOf(A,B)与Object.getPrototypeOf(A)
1、Object.setPrototypeOf(A,B)
作用:设置A的原型(proto)为B
let obj1 = {age:1};
let obj3 = {};
//如果希望obj3继承自obj1
Object.setPrototypeOf(obj3,obj1);
console.log(obj3);//{}
console.log(obj3.age);//1
为什么obj3是{},但是obj3.age却存在,为1?
其实大家知道原型链的同学应该很快就会发现,这个age虽然是obj3的属性,但是当无法从obj3上找到时,就会去查找原型链(注意:原型链的查找是根据proto查找的,而不是根据prototype查找的)。
那么,在原型链的哪里呢?
其实,只要知道setPrototypeOf是怎么实现的就能明白了,如下:
let obj1 = {age:1};
let obj3 = {};
//如果希望obj3继承自obj1
//Object.setPrototypeOf(obj3,obj1)就相当于如下:
obj3.__proto__ = obj1;
console.log(obj3);//{}
console.log(obj3.age);//1
2、Object.getPrototypeOf(A)
获取A的proto属性,与上面Object.setPrototypeOf(A,B)相反,在此不再赘述。
三、super
作用:继承被继承者的属性和方法
let obj1 = {age:1,getFood(){return '面包'}};
let obj3 = {};
obj3 = {
__proto__:obj1,
getFood(){
}
}
console.log(obj3.getFood());//undefined
说明,虽然我obj3继承了obj1,但是根据原型链查找规则,先会找到自身的属性和方法,再会去proto上查找
那如果在obj3的getFood中想要调用obj1的方法,该如何实现呢?
let obj1 = {age:1,getFood(){return '面包'}};
let obj3 = {};
obj3 = {
__proto__:obj1,
getFood(){
return '我今年'+super.age+'岁,我喜欢吃'+super.getFood();
}
}
console.log(obj3.getFood());//我今年1岁,我喜欢吃面包
可见,使用super,可以调用被继承者的属性和方法
四、类Class
大家应该都很熟悉ES5的面向对象的写法,那么针对ES6的面向对象,采用了Class类关键字,这种写法有什么好处呢?大家一起来感受下。
1、定义一个类
以前,JS里类和构造函数是一体的
类里面可以定义构造函数constructor,当创建一个类的实例的时候,就会调用构造函数
class Parent{
constructor(name){
this.name = name;//属于实例的私有属性
}
//以下是实例的共有属性,也就相当于原型上的属性
getName(){
console.log(this.name);
}
}
let p = new Parent('yuhua');
p.getName();
其实,这个如果转成es5,大家就能明白其原理:
"use strict";
var _createClass = (function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Parent = (function() {
function Parent(name) {
_classCallCheck(this, Parent);
this.name = name; //属于实例的私有属性
}
//以下是实例的共有属性,也就相当于原型上的属性
_createClass(Parent, [
{
key: "getName",
value: function getName() {
console.log(this.name);
}
}
]);
return Parent;
})();
var p = new Parent("yuhua");
p.getName();
这里用到了一个非常关键的技术点:defineProperties(>>关于defineProperties传送门<<)
对es5代码进行讲解:
这是整个代码的入口,仔细一看,其实就是返回了一个Parent,而这个Parent是什么?就是es5中的面向对象的形式——function Parent(){};那么,我们想要了解这个,就得了解这个Parent中的每一部分。
首先,我们来看function Parent中的内容
第一部分(先来看简单的):this.name = name这个就不需要解释了,将name通过Parent传入,并且挂在在this上,这是实例的私有属性。
第二部分:_classCallCheck()这个函数是什么作用呢?其实是做一个类的调用检查。具体如下:
参数:
instance 类的实例对象
Constructor 构造函数
关键语句:
instance instanceof Constructor 这个类的实例对象instance是否是构造函数Constructor的实例。确保它不被当做普通函数来调用。
整体:判定如果说这个传入的实例不是传入的这个构造函数的实例,那么就直接抛出错误。
然后,我们再来看看function Parent之外的_createClass
这个其实就是添加了我们es6中的getName方法,方法名getName就是key,这个方法内容就是value。
那么这个方法是如何实现的呢?我们来看下es5的写法:
其中Object.defineProperty才是真正给类的原型对象上添加属性。
那么,这呢,就是通过es5来实现es6的类。不仅要学会es6的类的使用,更高明白这其中的原理。
2、类的静态属性和方法
es6明确规定,Class内部只有静态方法,没有静态属性。
在方法前添加static关键字,这是定义类的静态方法
class Parent{
constructor(name){
this.name = name;//属于实例的私有属性
}
//以下是实例的共有属性,也就相当于原型上的属性
getName(){
console.log(this.name);
}
static getAge(){
console.log(18);
}
}
let p = new Parent('yuhua');
p.getName();
那么静态属性,在es5中是如何实现的?
其实很简单,上面所说的es5实现代码中,有一个_createClass方法,它的第三个参数就是静态方法
_createClass(
Parent,
[
{
key: "getName",
value: function getName() {
console.log(this.name);
}
}
],
[
{
key: "getAge",
value: function getAge() {
console.log(18);
}
}
]
);
五、类的继承
1、用extends和super关键字去继承
class Parent{
constructor(name){
this.name = name;
}
getName(){
console.log(this.name);
}
static getAge(){
console.log(18);
}
}
class Child extends Parent{
constructor(name,gender){
super(name);
this.gender = gender;
}
getGender(){
console.log(this.gender);
}
}
那么这个继承,是如何实现的呢?
咱们来看看es5的源码:
"use strict";
var _createClass = (function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return call && (typeof call === "object" || typeof call === "function")
? call
: self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError(
"Super expression must either be null or a function, not " +
typeof superClass
);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Parent = (function() {
function Parent(name) {
_classCallCheck(this, Parent);
this.name = name;
}
_createClass(
Parent,
[
{
key: "getName",
value: function getName() {
console.log(this.name);
}
}
],
[
{
key: "getAge",
value: function getAge() {
console.log(18);
}
}
]
);
return Parent;
})();
var Child = (function(_Parent) {
_inherits(Child, _Parent);
function Child(name, gender) {
_classCallCheck(this, Child);
var _this = _possibleConstructorReturn(
this,
(Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name)
);
_this.gender = gender;
return _this;
}
_createClass(Child, [
{
key: "getGender",
value: function getGender() {
console.log(this.gender);
}
}
]);
return Child;
})(Parent);
未完待续