vue2.6.11源码深度解析(一)
//1.导出不同格式的js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Vue = factory());
}(this, function () { 'use strict';
//2.创建一个不可修改的对象
var emptyObject = Object.freeze({});
//3. 6个isXXX名称的函数:这几个函数是用来各司其职的检查数据类型的
//第一.检查一个值是不是没有定义,这里检查了它是undefined或者null类型,满足其一就表示它已定义,返回true。
function isUndef (v) {
return v === undefined || v === null
}
//第二.检查一个值是不是定义了,必须同时满足它不是undefined类型且不是null类型。
function isDef (v) {
return v !== undefined && v !== null
}
//第三.检查一个值是不是true
function isTrue (v) {
return v === true
}
//第四.检查一个值是不是false
function isFalse (v) {
return v === false
}
/**
*第五.检查值是否为基础数据类型
*/
function isPrimitive (value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
/**
* 拓展: 为什么要检查一下obj !== null呢?因为虽然在js中Null与Object是两种数据类型,但是 typeof null 和 typeof {} 的结果是一样的, 所以这里要先排除null值的干扰
*/
//第六:检查是对象,但不是null 因为:typeof null 控制台运行的结果是"object"
function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
//4.获取一个值的原始类型字符串 e.g., [object Object],截取后,返回类型值,如 "Number", "Boolean", "Array"等
/**
* Get the raw type string of a value, e.g., [object Object].
* 作用: 获取一个值的原始类型字符串
* Object.prototype.toString.call(1) 控制台运行结果:"[object Number]"
Object.prototype.toString.call([]) 控制台运行结果:"[object Array]"
Object.prototype.toString.call(1).slice(8, -1) 控制台运行结果:"Number"
Object.prototype.toString.call([]).slice(8, -1)控制台运行结果:"Array"
拓展: prototype 属性使您有能力向对象添加属性和方法。eg: object.prototype.name=value
*/
var _toString = Object.prototype.toString;
function toRawType (value) {
return _toString.call(value).slice(8, -1)
}
//5.严格的类型检查,判断是不是对象
/**
* Strict object type check. Only returns true for plain JavaScript objects
* 拓展: 为什么特意加一句 Only returns true for plain JavaScript objects.呢?因为有一些值,虽然也属于js中的对象,但是有着更精确的数据类型,比如:
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(()=>{}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
...
plain 平的;简单的;朴素的;清晰的
*/
function isPlainObject (obj) {
return _toString.call(obj) === '[object Object]'
}
//6.作用: 检查一个值是不是正则表达式。
/* 拓展:
正则表达式不是对象吗?为什么不能直接使用typeof操作符检查呢? 这主要是处于兼容不同浏览器的考虑:
typeof /s/ === 'function'; // Chrome 1-12 Non-conform to ECMAScript 5.1
typeof /s/ === 'object'; // Firefox 5+ Conform to ECMAScript 5.1
*/
function isRegExp (v) {
return _toString.call(v) === '[object RegExp]'
}
//7.作用: 检查一个值是不是有效的数组索引,要满足:非负数, 整数, 有限大
/**
* Check if val is a valid array index.
* 拓展: 这主要是检查外来的值作为数组的索引的情况。
* isFinite(number) 函数用于检查其参数是否是无穷大。number 必需。要检测的数字。返回值 :如果 number 是有限数字(或可转换为有限数字),那么返回 true。否则,如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false。
* 解释:调用 String(val) 时,它只把 val 转换成原始的字符串,并返回转换后的值。
*/
function isValidArrayIndex (val) {
var n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
//8.判断是否是一个 Promise 对象
function isPromise (val) {
return (
isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
//9.作用: 把一个值转换成可以渲染的字符串
/**
* Convert a value to a string that is actually rendered. //单词 :convert 转换 rendered渲染
* 拓展: 第一个判断用的是 == 而不是 ===, 所以当值为undefined或者null时都会返回空串
解释:JSON.stringify() 方法将 JavaScript 对象 或者 数组 转换为字符串。
JSON.stringify接收三个参数,分别是要序列化的值, 要序列化的属性, 序列化所需的空格
解释:isPlainObject方法判定是否为一个对象 和val.toString值转成字符串是否等于Object.prototype.toString返回该对象的字符串;
: String(val)否则将val转成字符串
*/
//作用: 把一个值转换成可以渲染的字符串
function toString (val) {
return val == null
? ''
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, null, 2)
: String(val)
}
//10.作用: 把一个值转换成数字,转化成功,返回数字,否则原样返回。
/**
* Convert an input value to a number for persistence.
* If the conversion fails, return original string.
* 解释:isNaN() 函数用于检查其参数是否是非数字值。如果参数值为 NaN 或字符串、对象、undefined等非数字值则返回 true, 否则返回 false。
*/
function toNumber (val) {
var n = parseFloat(val);
return isNaN(n) ? val : n
}
//11.作用: 生成一个map,返回一个函数来检查一个键是不是在这个map中。
/**
* Make a map and return a function for checking if a key is in that map.
* 如果需要小写(expectsLowerCase = true)的话,就将 val 的值转换成小写再检查;否则直接检查
这里的检查,就是检查传入的 val(键), 是不是在map中,在的话会根据该键名,找到对应的值(true)
这里返回的是一个函数,该函数接收一个待检查的键名称,返回查找结果(true/undefined)
*/
function makeMap ( str, expectsLowerCase ) {
var map = Object.create(null);
var list = str.split(',');//将str 按 , 分割成字符串,组成新数组list
for (var i = 0; i < list.length; i++) {
map[list[i]] = true;
}//console.log(map);//如果 str = 'hello, world', 那么这个循环后{hello: true, " world": true}
return expectsLowerCase
? function (val) { return map[val.toLowerCase()]; }
: function (val) { return map[val]; }
}
/**
* Check if a tag is a built-in tag.作用: 检查一个标签是不是vue的内置标签
*/
var isBuiltInTag = makeMap('slot,component', true);//slot 位置;狭槽;水沟;硬币投币口
/**
* 检查一个属性是不是vue的保留属性
*/
var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');//scope 范围;余地;视野;眼界;导弹射程 ref 参考 。 reserved保留 预订(座位等);储备;拥有(某种权利);留出(一部分稍后使用);用于(特定场合);暂不作(判断或决定);把……专门留给;n. 储备(量);自然保护区;居住地;预备队;预备役部队;缄默;保留意见;储备金;(拍卖中的)底价;(印染中的)防染本色区
//12作用: 移除数组的某一项。
function remove (arr, item) {
if (arr.length) {
var index = arr.indexOf(item);//找到这一项在数组arr中的下标位置,找不到返回-1;eg: [12,23,45,46,465,555].indexOf(45) ==>2 ;[12,23,45,46,465,555].indexOf(2) ==>-1
if (index > -1) {//找到了
return arr.splice(index, 1)//eg: [12,23,45,46,465,555].splice(2,1) ==》[45] 返回移除后的这一项
}
}
}
//13.作用: 检查一个对象是否包含某属性
/**
* Check whether an object has the property.
* 拓展: 这里要思考的是,为什么不直接调用对象上的hasOwnProperty方法,反而要找对象原型上的呢?原因其实很简单,因为对象上的hasOwnProperty是可以被改写的,万一被重写了方法就无法实现这种检查了
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
//14.作用: 为一个纯函数创建一个缓存的版本。
/**
* Create a cached version of a pure function.
* 解析: 因为一个纯函数的返回值只跟它的参数有关,所以我们可以将入参作为key,返回值作为value缓存成key-value的键值对,这样如果之后还想获取之前同样参数的计算结果时,不需要再重新计算了,直接获取之前计算过的结果就行。
* 拓展: 纯函数:一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,则该函数可以称为纯函数。
解释:缓存的数据处理,只有当缓存中没有时才经过函数处理。
function cached(fn) {
var cache = Object.create(null);
return (
function cachedFn(str) {
var hit = cache[str];
console.log(cache);//{Frank: "Frank", Kai: "Kai"}
console.log(cache["Frank"]);//Frank
return hit || (cache[str] = fn(str))
}
)
}
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
});
//未缓存
var lastName = capitalize("Frank")
var firstName = capitalize("Kai")
//已缓存
var secondName = capitalize("Kai")
建议采用Chrome控制台的开发面板进行测试,6个变量命名处打断点,采用F10的形式,去查看每个变量命名时的完整调用过程,对比未缓存和已缓存处的call stack,就会立刻理解函数缓存的原理。
*/
function cached (fn) {//cached函数传一个参数fn方法,fn方法参数返回一个计算后的字符串
var cache = Object.create(null);//创建一个空对象cache
return (function cachedFn (str) {//返回一个闭包函数cachedFn,该函数的参数str是要处理的原字符串
var hit = cache[str];//定义一个变量hit保存cache[str]
return hit || (cache[str] = fn(str))//变量hit如果有对应的值,则直接返回,不再需要通过外层cached方法传递进来的fn方法的计算,反之,则需要计算后赋值给cache[str]同时返回。
})
}
//15.作用: 将连字符-连接的字符串转化成驼峰标识的字符串
/**
* Camelize a hyphen-delimited string.
* 解析: 可以通过此例来感受下上面的cached函数的作用,因为cache的参数, 一个转换连字符字符串的函数,是一个很纯很纯的函数,所以可以把它缓存起来。
* 使用: camelize('hello-world') // 'helloWorld'
* 替换关键词: 2种:
1. 简单替换: 将所有关键词,替换为相同的新值
str=str.replace(/正则/g,"新词")
2. 高级替换: 根据每个关键词的不同,动态生成新值替换ss
str=str.replace(/正则/g,function(kword){
return 根据kword的不同,返回不同新值
}); //return的结果会替换到当前kword所在位置
原理: 每发现一个关键词,就自动执行一次function
执行时,自动将本次找到的关键词,传给参数kword
函数的本质:
函数名其实时一个普通变量
函数其实是一个保存一段代码的对象
什么是: 封装一段代码段的对象
何时使用函数: 只要一段代码可能被重用!
如何: 3种:
1. 声明:
function 函数名(参数列表){
函数体;
return 返回值
}
问题: 会被声明提前
声明提前(hoist): 程序开始执行前,引擎会将var声明的变量和function声明的函数提前到当前作用域的顶部。赋值留在原地。
解决:
2. 用赋值方式:
var函数名=function (参数列表){
函数体;
return 返回值
}
优点: 不会被声明提前
揭示: 函数其实也是一个对象,函数名其实只是一个普通的变量。
3. 用new:-----了解
var 函数名=
new Function("参数","参数",…,"函数体;return 返回值")
*/
var camelizeRE = /-(\w)/g;// \w : 一位字母数字或下划线: \w [0-9A-Za-z_]
var camelize = cached(function (str) {
return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
});
//16.作用: 将一个字符串的首字母大写后返回,也是可以被缓存的函数。
/**
* Capitalize a string.
* 将一个字符串的首字母大写后返回
* 使用:capitalize('hello') // 'Hello'
* charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。
*/
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
});
想知更多解析,请等下回分解!谢谢
前端大全之Nodejs:免费分享前端技术文章,不定时前端干货发送
欢迎长按(扫描)二维码关注:前端大全之Nodejs
长按二维码