Learn-JS-from-Vue
What I learned from reading Vue's source code.
Object
Properties
At first, I thought those Javascript Objects without functions are simply key-value pairs, and properties are equal to keys. However, Javascript Object Property Descriptors successfully blew up my mind. What is that?
Property Descriptors
The Property Descriptors describes the trait of each property. There are 6 kinds of Property Descriptors.
-
configurable
Defines whether the descriptors could be modified for the property.
-
enumerable
Defines whether the property can be accessed by several enumerate functions. For example, when
enumerable
is set to false for one property, the property won't appear in the array returned by callingObject.keys
or accessed by thefor...in
loop. -
value
Defines the value for the property. Cannot be set together with
get
. This is what I firstly thought about JS Objects, simple key-value pairs. -
writable
Defines the mutability for the property, can only be set together with
value
. -
get
Accepts a function that would behave as the getter for the property. In Vue,
get
is used to gather UI, watch and computed property dependencies ofdata
object so that it can know which part of UI should be rerendered afterdata
modification. -
set
Accepts a function that would behave as the setter for the property. In Vue,
set
is used to notify all the dependencies to update when thedata
that they depends on has been set to a different value.
Related functions
Object.defineProperty(object, propertyName, options);
Pass the Property Descriptors as an Object into options to set the property in your style.
Object.getOwnPropertyDescriptor(object, propertyName);
Get the Descriptors for the specified property.
See more examples on MDN
Symbol Properties
Besides strings, Javascript Object properties can also be Symbols. The common pitfall for Symbol Properties is their enumerability. They cannot be accessed with Object.keys
or for...in
loop event if you explicitly set enumerable
descriptor to true
for the Symbol Property.
Related functions
Reflect.ownKeys(object);
Return all the Symbol and non-Symbol Properties
no matter they are enumerable
or not.
Object.getOwnPropertySymbols(object);
Return Symbol Properties
only, no matter they are enumerable
or not.
More about enumerability
Enumerability is important when you want to iterate through all or part the properties in any Objects. However, you still need to make sure whether you want to iterate thgouth the Object's own properties or properties also on its prototype chain.
Related functions
Object.keys(object);
Return non-Symbol Properties
that are enumerable
, for object's own properties only.
for (let prop in object) {
// Do something
}
Return non-Symbol Properties
that are enumerable
, for object's own properties and its prototype chain.
Object.getOwnPropertyNames(object);
Return all non-Symbol Properties
, for object's own properties only.
Prototype chain
It's surprising enough for beginners to know that Javascript is not a class based language. For detailed information, check MDN.
Inheritance in prototype fashion
It's totally different from inheritance in class based languages. Let's use the simplified example on MDN to get the main idea.
function A(a) {
this.a = a;
}
A.prototype.b = 2;
function B(a, b) {
A.call(this, a);
this.b = b;
this.c = 'c';
}
B.prototype = Object.create(A.prototype, {
c: {
enumerable: true,
configurable: true,
value: 3,
},
d: {
enumerable: true,
configurable: true,
value: 4,
},
});
B.prototype.constructor = B;
const a = new A(1);
// a: { a: 1, __proto__: { b: 2 }}
const b = new B('a', 'b');
// b: { a: "a", b: "b", __proto__: { c: 3, d: 4 }}
// c in function constructor B doesn't work
Why we need to re-assign Constructor.prototype.constructor?
Let's assume we have a Baby class.
function Baby() {
this.age = 0;
}
We can create a baby instance by calling new Baby()
. This would invoke the Baby.prototype.constructor
. When we define the Baby
constructor above, by default Baby.prototype.constructor
would be the function itself. However, after we call Baby.prototype = Object.create(Superclass.prototype)
, the constructor would be overwritten. Therefore, we need to re-assign Baby.prototype.constructor
to itself.
ES6 Classes
Fortunately, ES6 gives developers a class style way to define classes. However, it's only a syntax sugar on prototype chains.
What's the difference between prototype, __proto__ and Object.getPrototypeOf?
object.__proto__ and Object.getPrototypeOf(object)
They are same. However, object.__proto__ is not coined in any spec, yet most of the modern browsers supports it. These two would return the prototype for the specified object.
Func.prototype
This gives you a way to extend the prototype. This makes Mixin Pattern possible.
function A() {
this.a = 1;
this.b = 2;
}
const a1 = new A();
// a1: { a: 1, b: 2 }
A.ptototype.c = 3;
const a2 = new A();
// a1: { a: 1, b: 2, c: 3 }
// a2: { a: 1, b: 2, c: 3 }
The behavior of A.prototype = {...}
is unpredictable to me. Still trying to figure it out.
function A() {
this.a = 1;
this.b = 2;
}
let a1 = new A();
// a: { a: 1, b: 2}
A.prototype = {
c: 3,
};
let a2 = new A();
// a1: { a: 1, b: 2 }
// a2: { a: 1, b: 2, c: 3 }
A.prototype.d = 4;
let a3 = new A();
// a1: { a: 1, b: 2 } and both a.c, a.d are undefined
// a2: { a: 1, b: 2, c: 3, d: 4 }
// a3: { a: 1, b: 2, c: 3, d: 4 }
Constructor and Prototype
The prototype property is one of the most confusingly-named parts of JavaScript -- MDN
Constructor
Define properties (includes functions) that should not be inherited.
function Person(age) {
this.type = 'Human';
this.age = age;
}
Prototype
Define properties (includes functions) that need to be inherited.
Person.prototype.greeting = function() {
console.log('hello');
};
Example
Use the Person class that we've defined above.
function Teacher(age, years) {
Person.call(this, age);
this.years = years;
}
Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;
const tim = new Teacher(39, 5);
console.log(tim.type); // undefined
tim.greeting(); // "hello"