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}//第三.检查一个值是不是truefunction isTrue (v) {return v === true}//第四.检查一个值是不是falsefunction 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.1typeof /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 按 , 分割成字符串,组成新数组listfor (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) ==>-1if (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"]);//Frankreturn 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);//创建一个空对象cachereturn (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. 高级替换: 根据每个关键词的不同,动态生成新值替换ssstr=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
长按二维码
